上面代码中的contexts保存了所有的命名空间,值为下划线(_)的contextName是其中的一个默认的命名空间;
newContext函数里面封装了各种工具方法和Module类。 1.工具方法
function trimDots(ary) {...}//处理路径中含有的.和.. function normalize(name, baseName, applyMap) {...} function removeScript(name) {...}//删除script节点 function hasPathFallback(id) {...} function splitPrefix(name) {...} function removeProjectPrefix(name) {...} function getProjectUrl(parentName) {...} function buildFinalUrl(fullName,prefix) {...} //生成一个模块对象 function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {...} //从registry根据模块ID取模块对象,如果取不到新建一个模块 function getModule(depMap) {...} //这个on方法会调用Module中的on方法 function on(depMap, name, fn) {...} function onError(err, errback) {...}2.Module类
Module = function (map) {...} /* this.exports this.factory this.depMaps = [], this.enabled, this.fetched */ Module.prototype = { //初始化模块 init: function (depMaps, factory, errback, options) {...} //如果一个模块有多个依赖模块,一个依赖模块加载完,depCount值会减1; //当模块的所有依赖模块加载完毕后,才通过this.emit('defined', this.exports);触发模块加载完成事件。 defineDep: function (i, depExports) {...} fetch: function () {...} load: function () {...} check: function () {...} callPlugin: function () {...} enable: function () {...} on: function (name, cb) {...} emit: function (name, evt) {...} } newContext函数内的context对象,作为newContext的返回值 context = { configure: function (cfg) {...} makeRequire: function (relMap, options) {...} enable: function (depMap) {...} load: function (id, url) {...} execCb: function (name, callback, args, exports) {...} onScriptLoad: function (evt) {...} onScriptError: function (evt) {...} } //makeRequire函数是上面context 中定义的函数 context.require = context.makeRequire();实例中data-main属性值为config.js,下面源码中的req(cfg)为require执行的第二个函数,执行后会加载config.js
//Look for a data-main script attribute, which could also adjust the baseUrl. //入口的main.js的路径作为基准路径 if (isBrowser && !cfg.skipDataMain) { //Figure out baseUrl. Get it from the script tag with require.js in it. eachReverse(scripts(), function (script) { //Set the 'head' where we can append children by //using the script's parent. if (!head) { head = script.parentNode; } //Look for a data-main attribute to set main script for the page //to load. If it is there, the path to data main becomes the //baseUrl, if it is not already set. dataMain = script.getAttribute('data-main'); if (dataMain) { //Preserve dataMain in case it is a path (i.e. contains '?') mainScript = dataMain; //Set final baseUrl if there is not already an explicit one. if (!cfg.baseUrl) { //Pull off the directory of data-main for use as the //baseUrl. src = mainScript.split('/'); mainScript = src.pop(); subPath = src.length ? src.join('/') + '/' : './'; cfg.baseUrl = subPath; } //Strip off any trailing .js since mainScript is now //like a module name. mainScript = mainScript.replace(jsSuffixRegExp, ''); //If mainScript is still a path, fall back to dataMain if (req.jsExtRegExp.test(mainScript)) { mainScript = dataMain; } //Put the data-main script in the files to load. cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; return true; } }); } //cfg为自执行匿名函数内的变量 req(cfg);context.nextTick的函数定义如下:
req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { setTimeout(fn, 4); } :生成的异步任务会放入事件队列中,等待主线程执行(事件轮询机制)
上面会创建一个script节点放入head节点中,等主线程空闲时会去任务队列中取出来加载js文件,执行其中的代码;
加载完config.js后,会加载main.js文件,其他的模块加载机制类似(有时间再丰富)
注意点: 三个函数作用域
全局作用域(function (global) {}(this) 自执行函数的作用域nexContext函数的作用域