(function( userConfig,defaultConfig ){ /* summary: 概述 This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with any AMD-compliant loader via the package main module dojo/main. 这个是一个源
文件加载器,在dojo的开发过程中,它是整个应用程序的入口。 你也可以通过其它遵循AMD规范的加载器来加载Dojo,如requirejs. 如果你采用第三方的加截器,可以不用使用本
文件,而是直接加载 dojo/main 模块 description: 描述 This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured to operate in either synchronous or asynchronous modes. After the loader is defined,dojo is loaded IAW the package main module dojo/main. In the event you wish to use a foreign loader,you may load dojo as a package via the package main module dojo/main and this loader is not
required; see dojo/package.json for details. 这是Dojo的一个源
文件加载器,它遵循 AMD规范,可以配置成同步或异步模式。当加载器被定义好后(即用于浏览器,nodejs环境), dojo的主体会被加载进来,这主要是依据 dojo/main模块。如果想使用一个外部的 加载器(如requirejs),你可以直接将 dojo/main进行加载,而这个loader不必加载;需要了解更多,请查看 dojo/package.json; In order to keep compatibility with the v1.x line,this loader includes additional machinery that enables the dojo.provide,dojo.require et al API. This machinery is loaded by default,but may be dynamically removed via the has.js API and statically removed via the build system. 考虑到向前兼容dojo的版本, 这个加载器包含一些额外机制,如使 dojo.provide,dojo.requre; 这些机制默认情况下是包含的,但可以通过 has.js 的API动态
删除或通过 dojo的build system 手动
删除。 This loader includes sniffing machinery to determine the environment; the following environments are supported: 这个加载器也包含嗅探机制去发现当前使用的环境, 以下三种会被
支持。 - browser - node.js - rhino Java 编写的一个解释器,由 Mozilla 开发 This is the so-called "source loader". As such,it includes many optional features that may be discarded by building a customized version with the build system. 这就是所谓的源
文件加载器,如上陈述, 它可能通过 build system可以
去除掉一些
功能,而形成你自己想要的版本。. Design and Implementation Notes 设计和实现日记 This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) loader that can be configured to operate in either synchronous or asynchronous modes. 这个
功能定议一个遵循AMD规范(http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)的加载器。 可以配置成同步或异步加载模式。 ! 有空了解下AMD规范 here is a road map of the contents: 以下是这个
内容的路线图。 1. Small library for use implementing the loader. 加载器会使用到的一个很小的库,即工具
函数。 2. Define the has.js API; this is used throughout the loader to bracket features.定义一个has.js API; 整个加载器都会使用到它来特征检测 3. Define the node.js and rhino sniffs and sniff. 定义侦测node.js 及rhino 的嗅探器 4. Define the loader's data. 定义一个loader的数据模型 5. Define the configuration machinery. 实现配置机制 6. Define the script element sniffing machinery and sniff for configuration data. 定义一个角本元素嗅探机制,并且将嗅探的结果作为配置数据 7. Configure the loader IAW the provided user,default,and sniffing data. 依据提供的
用户,默认参数及嗅探到的信息,配置一个加载器 8. Define the global require function. 定义一个全局加载
函数 9. Define the module resolution machinery 定义一个模块解析机制. 10. Define the module and plugin module definition machinery 定义一个模块及
插件开发规范 11. Define the script injection machinery. 定义一个角本注入机制 12. Define the window load detection. 定义一个window 加载侦测 13. Define the logging API. 定义一个日志API 14. Define the tracing API. 定义一个跟踪API,即调试用的API 15. Define the AMD define function. 实现AMD规范中需要定义的
函数 16. Define the dojo v1.x provide/require machinery--so called "legacy" modes. 为了兼容之前老的dojo,定义provide/ require。 17. Publish global variables. 发布一些
全局变量 Language and Acronyms and I
dioms 束语 moduleId: a CJS(CommonJS) module identifier,(used for public APIs 对外的CommonJS 模块标识符 mid: moduleId (used internally) 对内的模块ID(标识符) packageId: a package identifier (used for public APIs) 对外的包标识符 pid: packageId (used internally); the implied system or default package has pid==="" 对内的包id,如是是默认的包,则pid为空字符 pack: package is used internally to reference a package object (since javascript has reserved words including "package") 包对象 prid: plugin resource identifier
插件标识符 referenceModule: 参照模块,用
代码来表示更好理解,require['dojo/dom'],而 dom里面依赖 define(["./sniff","./_base/window"],sniff,window. 在加载sniff时, referenceModule代表的就是dom 模块. The integer constant 1 is used in place of true and 0 in place of false 常量1及0,分别用作ture及false的占位符 */ var noop = function(){ },isEmpty = function(it){ for(var p in it){ return 0; } return 1; },toString = {}.toString,isFunction = function(it){ return toString.call(it) == "[object Function]"; },isString = function(it){ return toString.call(it) == "[object String]"; },isArray = function(it){ return toString.call(it) == "[object Array]"; },forEach = function(vector,callback){ if(vector){ for(var i = 0; i < vector.length;){ callback(vector[i++]); } } },mix = function(dest,src){ for(var p in src){ dest[p] = src[p]; } return dest; },makeError = function(error,info){ return mix(new Error(error),{src:"dojoLoader",info:info}); },uidSeed = 1,uid = function(){ // Returns a unique identifier (within the lifetime of the document) of the form /_d+/. 返回一个唯一标识符(文档的生命周期内),格式为 “_数字” 形式 return "_" + uidSeed++; },// FIXME: how to doc window.require() api FIXME 标识此注释的
内容代码需要修正 TODO 标识此处
功能需要实现, 这里说的是需要修正 require的api 文档 // this will be the global require function; // 全局的 require
函数 req = function( config,//(object,optional) hash of configuration properties 传入的第一个值为配置
属性的散列对象(可选) dependencies,//(array of commonjs.moduleId,optional) list of modules to be loaded before applying callback 传入的第二个参数为,在
调用回调
函数之前,需要加载的模块id,该模块id遵循commonjs模范(可选) callback //(function,optional) lambda expression to apply to module values implied by dependencies,传入一个匿名
函数,当依赖的模块加载完后,会
调用这个
函数 ){ return contextRequire(config,dependencies,callback,req); },// the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout loader 通过has.js API来包含或者排除某些特征 global = this,doc = global.document,element = doc && doc.createElement("DiV"),has = req.has = function(name){ return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global,doc,element)) : hasCache[name]; },hasCache = has.cache = defaultConfig.hasCache; has.add = function(name,test,now,force){ //
添加某个测试。 (hasCache[name]===undefined || force) && (hasCache[name] = test); // 测试缓存中还没有测试结果,或者要强制覆盖原有的测试, hasCache[name]=test; return now && has(name); //指定第三个参数为true时,立即返回检测的结果。 }; /* 以下测试是否为 nodejs环境,如果是,加载./_base/configNode.js的
文件。 如果
用户的配置中有指定has对象,并且存在"host-node",那么直接返回
用户指定的值。 惹没有指定,则测试是否有全局对象process,并测试它的版本是否为node. 例: nodejs环境 hasCache['host-node'] = 1 在这里,没有设定has.add的第三个参数。所以只是设置了hasCache['host-node']的值,而不返回,在if中,会通过has("host-node")来再获得相应的值。 */ has.add("host-node",userConfig.has && "host-node" in userConfig.has ? userConfig.has["host-node"] : (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); if(has("host-node")){ // fixup the default config for node.js environment require("./_base/configNode.js").config(defaultConfig); // remember node's require (with respect to baseUrl==dojo's root) defaultConfig.loaderPatch.nodeRequire = require; } /* 用于检测rhino环境 */ has.add("host-rhino",userConfig.has && "host-rhino" in userConfig.has ? userConfig.has["host-rhino"] : (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); if(has("host-rhino")){ // owing to rhino's lame feature that hides the source of the script,give the user a way to specify the baseUrl... for(var baseUrl = userConfig.baseUrl || ".",arg,rhinoArgs = this.arguments,i = 0; i < rhinoArgs.length;){ arg = (rhinoArgs[i++] + "").split("="); if(arg[0] == "baseUrl"){ baseUrl = arg[1]; break; } } load(baseUrl + "/_base/configRhino.js"); rhinoDojoConfig(defaultConfig,baseUrl,rhinoArgs); } // userConfig has tests override defaultConfig has tests; do this after the environment detection because // the environment detection usually sets some has feature values in the hasCache. //
用户配置中的has测试会覆盖 defaultConfig 中的 has测试。 但这会发现在环境侦测之后, 因为环境侦没通常会在hasCache里设置特征检测的值。 // 总体的意思就是在确定loader使用环境后,用 userConfig中的has 替换defaultConfig指定的has测试 for(var p in userConfig.has){ has.add(p,userConfig.has[p],1); } // // define the loader data 定义加载器的数据 // // the loader will use these like symbols if the loader has the traceApi; otherwise // define magic numbers so that modules can be provided as part of defaultConfig /* 如果有指定traceApi(跟踪
功能),定义的变量相当于标识的符号,表示模块是在请求,到达,正在执行,执行完成等各种状态。否则,定义的数字,模块可以作为defaultConfig的一部分。 以下变量指定模块当前的状态 */ var requested = 1,arrived = 2,nonmodule = 3,executing = 4,executed = 5; if(has("dojo-trace-api")){ // these make debugging nice; don't do it for production code 使调试变得容易,不要将他用于产品
代码。 requested = "requested"; arrived = "arrived"; nonmodule = "not-a-module"; executing = "executing"; executed = "executed"; } var legacyMode = 0,sync = "sync",xd = "xd",syncExecStack = [],dojoRequirePlugin = 0,checkDojoRequirePlugin = noop,transformToAmd = noop,getXhr; // 是否可以使用1.7之前的加载器 if(has("dojo-sync-loader")){ req.isXdUrl = noop; req.initSyncLoader = function(dojoRequirePlugin_,checkDojoRequirePlugin_,transformToAmd_){ // the first dojo/_base/loader loaded gets to define these variables; they are designed to work // in the presence of zero to many mapped dojo/_base/loaders if(!dojoRequirePlugin){ dojoRequirePlugin = dojoRequirePlugin_; checkDojoRequirePlugin = checkDojoRequirePlugin_; transformToAmd = transformToAmd_; } return { sync:sync,requested:requested,arrived:arrived,nonmodule:nonmodule,executing:executing,executed:executed,syncExecStack:syncExecStack,modules:modules,execQ:execQ,getModule:getModule,injectModule:injectModule,setArrived:setArrived,signal:signal,finishExec:finishExec,execModule:execModule,dojoRequirePlugin:dojoRequirePlugin,getLegacyMode:function(){return legacyMode;},guardCheckComplete:guardCheckComplete }; }; if(has("dom")){ // in legacy sync mode,the loader needs a minimal XHR library var locationProtocol = location.protocol,locationHost = location.host; req.isXdUrl = function(url){ if(/^\./.test(url)){ // begins with a dot is always relative to page URL; therefore not xdomain return false; } if(/^\/\//.test(url)){ // for v1.6- backcompat,url starting with // indicates xdomain return true; } // get protocol and host // \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file // locationHost is falsy if file protocol => if locationProtocol matches and is "file:",|| will return false var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/); return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost)); }; // note: to get the file:// protocol to work in FF,you must set security.fileuri.strict_origin_policy to false in about:config has.add("dojo-xhr-factory",1); has.add("dojo-force-activex-xhr",has("host-browser") && !doc.addEventListener && window.location.protocol == "file:"); has.add("native-xhr",typeof XMLHttpRequest != "undefined"); if(has("native-xhr") && !has("dojo-force-activex-xhr")){ getXhr = function(){ return new XMLHttpRequest(); }; }else{ // if in the browser an old IE; find an xhr for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP','Microsoft.XMLHTTP','Msxml2.XMLHTTP.4.0'],progid,i = 0; i < 3;){ try{ progid = XMLHTTP_PROGIDS[i++]; if(new ActiveXObject(progid)){ // this progid works; therefore,use it from now on break; } }catch(e){ // squelch; we're just trying to find a good ActiveX progid // if they all fail,then progid ends up as the last attempt and that will signal the error // the first time the client actually tries to exec an xhr } } getXhr = function(){ return new ActiveXObject(progid); }; } req.getXhr = getXhr; has.add("dojo-gettext-api",1); req.getText = function(url,async,onLoad){ var xhr = getXhr(); xhr.open('GET',fixupUrl(url),false); xhr.send(null); if(xhr.status == 200 || (!location.host && !xhr.status)){ if(onLoad){ onLoad(xhr.responseText,async); } }else{ throw makeError("xhr
Failed",xhr.status); } return xhr.responseText; }; } }else{ req.async = 1; } // // loader eval // var eval_ = // use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution 使用
函数的构造
函数,便我们定义的eval形成闭包,不会污染全局作域用。 new Function('return eval(arguments[0]);'); req.eval = function(text,hint){ return eval_(text + "\r\n////@ sourceURL=" + hint); }; // // loader micro events API // loader 内部事件系统 var listenerQueues = {},error = "error",signal = req.signal = function(type,args){ var queue = listenerQueues[type]; // notice we run a copy of the queue; this allows listeners to add/remove // other listeners without affecting this particular signal forEach(queue && queue.slice(0),function(listener){ listener.apply(null,isArray(args) ? args : [args]); }); },on = req.on = function(type,listener){ // notice a queue is not created until a client actually connects var queue = listenerQueues[type] || (listenerQueues[type] = []); queue.push(listener); return { remove:function(){ for(var i = 0; i<queue.length; i++){ if(queue[i]===listener){ queue.splice(i,1); return; } } } }; }; // configuration machinery; with an optimized/built defaultConfig,all configuration machinery can be discarded // lexical variables hold key loader data structures to help with minification; these may be completely,// one-time initialized by defaultConfig for optimized/built versions // 实现loader的配置机制; var aliases // a vector of pairs of [regexs or string,replacement] => (alias,actual) = [],paths // CommonJS paths = {},pathsMapProg // list of (from-path,to-path,regex,length) derived from paths; // a "program" to apply paths; see computeMapProg = [],packs // a map from packageId to package configuration object; see fixupPackageInfo = {},map = req.map // AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map = {},mapProgs // vector of quads as described by computeMapProg; map-key is AMD map key,map-value is AMD map value = [],modules // A hash:(mid) --> (module-object) the module namespace // // pid: the package identifier to which the module belongs (e.g.,"dojo"); "" indicates the system or default package // mid: the fully-resolved (i.e.,mappings have been applied) module identifier without the package identifier (e.g.,"dojo/io/script") // url: the URL from which the module was retrieved // pack: the package object of the package to which the module belongs // executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed // deps: the dependency vector for this module (vector of modules objects) // def: the factory for this module // result: the result of the running the factory for this module // injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define // load: plugin load function; applicable only for plugins // // Modules go through several phases in creation: // // 1. Requested: some other module's definition or a require application contained the requested module in // its dependency vector or executing code explicitly demands a module via req.require. // // 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL // // 3. Loaded: the resource injected in [2] has been evaluated. // // 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some // resources may just contain a bundle of code and never formally define a module via define // // 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result. = {},cacheBust // query string to append to module URLs to bust browser cache = "",cache // hash:(mid | url)-->(function | string) // // A cache of resources. The resources arrive via a config.cache object,which is a hash from either mid --> function or // url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided // by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided // by config.cache always have a function value that causes the same code to execute as if the module was script injected. // // Both kinds of key-value pairs are entered into cache via the function consumePendingCache,which may relocate keys as given // by any mappings *iff* the config.cache was received as part of a module resource request. // // Further,for mid keys,the implied url is computed and the value is entered into that key as well. This allows mapped modules // to retrieve cached items that may have arrived consequent to another namespace. // = {},urlKeyPrefix // the prefix to prepend to a URL key in the cache. = "url:",pendingCacheInsert // hash:(mid)-->(function) // // Gives a set of cache modules pending entry into cache. When cached modules are published to the loader,they are // entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another // independent set of cached modules. (1) is the usual case,and this case allows normalizing mids given in the pending // cache for the local configuration,possibly relocating modules. = {},dojoSniffConfig // map of configuration variables // give the data-dojo-config as sniffed from the document (if any) = {},insertPointSibling // the nodes used to locate where scripts are injected into the document = 0; // 确保在 dojo在 build时可配置。提供配置API,这样可以将map或者paths中的值处理为 mapProgs,pathsMapProgs,主要是在编译时,这些配置信息都是存放在profile
文件里,可以查看profile
文件的格式。 Ensures that the build is configurable if(has("dojo-config-api")){ var consumePendingCacheInsert = function(referenceModule){ var p,item,match,m; for(p in pendingCacheInsert){ item = pendingCacheInsert[p]; match = p.match(/^url\:(.+)/); if(match){ cache[urlKeyPrefix + toUrl(match[1],referenceModule)] = item; }else if(p=="*now"){ now = item; }else if(p!="*noref"){ m = getModuleInfo(p,referenceModule,true); cache[m.mid] = cache[urlKeyPrefix + m.url] = item; } } if(now){ now(createRequire(referenceModule)); } pendingCacheInsert = {}; },escapeString = function(s){ return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,function(c){ return "\\" + c; }); },computeMapProg = function(map,dest){ // This routine takes a map as represented by a JavaScript object and initializes dest,a vector of // quads of (map-key,map-value,refex-for-map-key,length-of-map-key),sorted decreasing by length- // of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning // of a the search source. Notice the map-value is irrelevant to the algorithm /* 这段程序获得一个由Javascript 对像表示的map配置数据, 然后会初始化dest(存放结果的数组),dest中存放的值都是四维向量(map-key,map-key的正则表达式, map-key的长度) 程序会对dest中的值,根据map-key的长度进行降序排序. 正则表达式用于
搜索目标对像的开头是否是may-key + "/" 或者may-key + "$" (完全匹配). 注意,map-value跟算法没有什么关系。 */ /* map: { myOldApp: { dojo: "dojo16",dijit: "dijit16",dojox: "dojox16" } } [["myOldApp",Object { dojo="dojo16",dijit="dijit16",dojox="dojox16"},RegExp /^myOldApp(\/|$)/,8]] paths:{ "a/b": "myApp/ core/widget" } [["a/b","myApp/ core/widget",RegExp /^a\/b(\/|$)/,3]] */ dest.splice(0,dest.length); for(var p in map){ dest.push([ p,map[p],new RegExp("^" + escapeString(p) + "(\/|$)"),p.length]); } dest.sort(function(lhs,rhs){ return rhs[3] - lhs[3]; }); return dest; },computeAliases = function(config,dest){ /* 将键值对,转化为正则表达式形式的键值对 aliases:[ ["text","dojo/text"] ] [[RegExp /^text$/,"dojo/text"]] */ forEach(config,function(pair){ // take a fixed-up copy... //如果是字符串,则构造一个正则表达式,如果是
函数或者正则表达式,直接使用键。 dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0],pair[1]]); }); },fixupPackageInfo = function(packageInfo){ // calculate the precise (name,location,main,mappings) for a package 计算每个包的准确信息,包的信息
包括,名字,位置, 主
文件名称, 映射情况。 /* packages: [ {location:"lib/dojo"},// 标注1 { name: "dojo16",location: "lib/dojo16" },{ name: "dijit16",location: "lib/dijit16" },{ name: "dojox16",location: "lib/dojox16" },{ name: "dojo",location: "lib/dojo" },{ name: "dijit",location: "lib/dijit" },{ name: "dojox",location: "lib/dojox" },{name:"doh",location:"lib/util/doh"},{ name: "myOldApp",location: "myOldApp",packageMap:{dojo:"dojo16"} },{ name: "my",location: "my" } ] */ var name = packageInfo.name; if(!name){ // packageInfo must be a string that gives the name // 如果没有在包中指定name,则整个包对像作为包的
名称, 如标注1, name= {location:"lib/dojo"} name = packageInfo; packageInfo = {name:name}; } packageInfo = mix({main:"main"},packageInfo); //mix(dest,src) packageInfo.location = packageInfo.location ? packageInfo.location : name; //如果没有指定包的位置,刚location为包的
名称。 // packageMap is deprecated in favor of AMD map, packageMap已经被弃用,被map配置替代 if(packageInfo.packageMap){ map[name] = packageInfo.packageMap; //将packageMap值作为 map对像中的一个值。 } if(!packageInfo.main.indexOf("./")){ packageInfo.main = packageInfo.main.substring(2); //如果main以"./"开头,则去掉"./" } // now that we've got a fully-resolved package object,push it into the configuration // 现在我们获得一个完全解析后的一个包对像,即原始的包对像可能只有
名称和location,处理后的包对像,都有 name,main
属性,有的还有packageMap,packageMap不是很重要,在这里只是将他的值
添加到map里。 packs[name] = packageInfo; },delayedModuleConfig // module config cannot be consumed until the loader is completely initialized; therefore,all // module config detected during booting is memorized and applied at the end of loader initialization // TODO: this is a bit of a kludge; all config should be moved to end of loader initialization,but // we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete = [],config = function(config,booting,referenceModule){ for(var p in config){ if(p=="waitSeconds"){ req.waitms = (config[p] || 0) * 1000; //设置加载一个模块的超时时间, 单位为秒. } if(p=="cacheBust"){ cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; //
添加到URL后缀的字符串。以打断浏览器缓存。 } if(p=="baseUrl" || p=="combo"){ req[p] = config[p]; //设置baseUrl和 combo的值( dojo-combo-api是启动一些老版本的API,可以不用设置) } if(has("dojo-sync-loader") && p=="async"){ // falsy or "sync" => legacy sync loader // "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable,set automatically by the loader) // "legacyAsync" => permanently in "xd" by choice // "debugAtAllCosts" => trying to load everything via script injection (not implemented) // otherwise,must be truthy => AMD // legacyMode: sync | legacyAsync | xd | false //配置加载器的工作模式,AMD,同步,或者跨域异步 var mode = config[p]; req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false)); req.async = !legacyMode; } if(config[p]!==hasCache){ //将defautlConfig,dojoConfig,dojoSniffConfig中的配置数据存入rawConfig. // 将原生配置存入hasCache // accumulate raw config info for client apps which can use this to pass their own config req.rawConfig[p] = config[p]; p!="has" && has.add("config-"+p,config[p],booting); } } // make sure baseUrl exists 确保存在 baseUrl if(!req.baseUrl){ req.baseUrl = "./"; } // make sure baseUrl ends with a slash 确保baseUrl是以"/"结尾,即表示一个
文件夹路径 if(!/\/$/.test(req.baseUrl)){ req.baseUrl += "/"; } // now do the special work for has,packages,packagePaths,paths,aliases,and cache // 开始具体的配置工作,如has,mapProgs,pathsMapProg. // 如果在userConfig,defaultConfig中指定了has测试, 则把相应的has测试
添加到hasCache中。 for(p in config.has){ /* var dojoConfig={ has: { "dojo-firebug": true } } */ has.add(p,config.has[p],booting); } //如果指定了packages,对每个包进行处理,使每个包有固定的格式{name:**,location:**,main:**},如果有packageMap
属性,则把值
添加到map配置里. 详细请查看fixupPackageInfo. forEach(config.packages,fixupPackageInfo); // packagePath已经被弃用,会在2.0移除. /* var dojoConfig = { packagePaths:{ "path/to/some/place":[ "myPackage",{ name:"yourPackage",main:"base" } ] } } 相当于 packages:[{ name:"myPackage",location:"path/to/some/place/myPackage" },location:"path/to/some/place/youPackage" }] */ for(baseUrl in config.packagePaths){ forEach(config.packagePaths[baseUrl],function(packageInfo){ var location = baseUrl + "/" + packageInfo; //如果只指定了一个字符串,如说明中的"myPackage" if(isString(packageInfo)){ packageInfo = {name:packageInfo}; } packageInfo.location = location; fixupPackageInfo(packageInfo); }); } // notice that computeMapProg treats the dest as a reference; therefore,if/when that variable // is published (see dojo-publish-privates),the published variable will always hold a valid value. // this must come after all package processing since package processing may mutate map /* 注意computeMapProg 会将结果直接保存到dest,如果变量被发布(可以通过require直接获得),那么发布的变量会直接获得这个有效值 这个步骤必须在所有的package处理完成后才能进行,因为在包的处理过程中,会改变map的值。可以看上面的 packageMap. map: { myOldApp: { dojo: "dojo16",dojox: "dojox16" } } [["myOldApp",8]] */ computeMapProg(mix(map,config.map),mapProgs); /* 对上面的 may-key 进一步处理,即 Object { dojo="dojo16",dojox="dojox16"} 也转化为一个四维向量数组 [["myOldApp",8]] */ forEach(mapProgs,function(item){ item[1] = computeMapProg(item[1],[]); if(item[0]=="*"){ mapProgs.star = item; } }); /* 计算路径映射 paths:{ "a/b": "myApp/ core/widget" } [["a/b",3]] */ computeMapProg(mix(paths,config.paths),pathsMapProg); // aliases /* aliases:[ ["text","dojo/text"] ] [[RegExp /^text$/,"dojo/text"]] 1. 如果是字符串,别名需要跟目标字符串完全匹配 2. 别名可以采用正则表达式的方式. */ computeAliases(config.aliases,aliases); //给某个模块传递配置。如果指定了booting为1,刚在loader启动时,不进行模块配置。 // 在实际开发中,好像都不会对模块进行配置。可以忽略delayedModuleConfig 和在 dojoConfig={ config:{模块
名称: 配置}} if(booting){ delayedModuleConfig.push({config:config.config}); }else{ for(p in config.config){ var module = getModule(p,referenceModule); module.config = mix(module.config || {},config.config[p]); } } // push in any new cache values if(config.cache){ /* 不能理解 cache,pedingCaccheInsert,consumePendingCacheInsert. 没有找到相关的使用例子. */ consumePendingCacheInsert(); pendingCacheInsert = config.cache; if(config.cache["*noref"]){ consumePendingCacheInsert(); } } signal("config",[config,req.rawConfig]); }; // // execute the var
IoUs sniffs; userConfig can override and value // // 可以获得浏览器 user agent的信息 if(has("dojo-cdn") || has("dojo-sniff")){ // the sniff regex looks for a src attribute ending in dojo.js,optionally preceded with a path. // match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the // dojo location on CDN deployments and baseUrl when either/both of these are not provided // explicitly in the config data; this is the 1.6- behavior. var scripts = doc.getElementsByTagName("script"),i = 0,script,dojoDir,src,match; while(i < scripts.length){ script = scripts[i++]; if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){ // sniff dojoDir and baseUrl dojoDir = match[3] || ""; defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir; // remember an insertPointSibling insertPointSibling = script; } // sniff configuration on attribute in script element if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){ dojoSniffConfig = req.eval("({ " + src + " })","data-dojo-config"); // remember an insertPointSibling insertPointSibling = script; } // sniff requirejs attribute if(has("dojo-requirejs-api")){ if((src = script.getAttribute("data-main"))){ dojoSniffConfig.deps = dojoSniffConfig.deps || [src]; } } } } //
禁止将 doh(单元测试)里的配置, 混合到dojoSniffConfig ( dojo.js 中 data-dojo-config扫描到的配置) if(has("dojo-test-sniff")){ // pass down doh.testConfig from parent as if it were a data-dojo-config try{ if(window.parent != window && window.parent.require){ var doh = window.parent.require("doh"); doh && mix(dojoSniffConfig,doh.testConfig); } }catch(e){} } // configure the loader; let the user override defaults req.rawConfig = {}; config(defaultConfig,1); // do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides if(has("dojo-cdn")){ packs.dojo.location = dojoDir; if(dojoDir){ dojoDir += "/"; } packs.dijit.location = dojoDir + "../dijit/"; packs.dojox.location = dojoDir + "../dojox/"; } config(userConfig,1); config(dojoSniffConfig,1); }else{ // no config API,assume defaultConfig has everything the loader needs...for the entire lifetime of the application paths = defaultConfig.paths; pathsMapProg = defaultConfig.pathsMapProg; packs = defaultConfig.packs; aliases = defaultConfig.aliases; mapProgs = defaultConfig.mapProgs; modules = defaultConfig.modules; cache = defaultConfig.cache; cacheBust = defaultConfig.cacheBust; // remember the default config for other processes (e.g.,dojo/config) req.rawConfig = defaultConfig; } if(has("dojo-combo-api")){ // 已被弃用 req.combo = req.combo || {add:noop}; var comboPending = 0,combosPending = [],comboPendingTimer = null; } // build the loader machinery iaw configuration,including has feature tests 依据配置数据,和特征检测,建立加载器的核
功能 var injectDependencies = function(module){ // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies /*
方法签名:需要注入依赖
链接的模块对像 */ guardCheckComplete(function(){ forEach(module.deps,injectModule); if(has("dojo-combo-api") && comboPending && !comboPendingTimer){ comboPendingTimer = setTimeout(function() { comboPending = 0; comboPendingTimer = null; req.combo.done(function(mids,url) { var onLoadCallback= function(){ // defQ is a vector of module definitions 1-to-1,onto mids runDefQ(0,mids); checkComplete(); }; combosPending.push(mids); injectingModule = mids; req.injectUrl(url,onLoadCallback,mids); injectingModule = 0; },0); } }); },contextRequire = function(a1,a2,a3,contextRequire){ /* a1: 一般为config object,配置对像, 可以通过require对loader进行配置. a2:
函数的依赖数组。 a3: 加载完依赖后,要执行的回调
函数 referenceModule: 引用模块,举例来说,如dom.js里面, define(["./sniff",当要加载dom模块时,需要先加载sniff及 _base/window,那么在这时相对于sniff时,引用模块就是dojo/dom,而dom的引用模块是全局
函数require,那么引用模块为0 contextRequire: 加载器的上下文,如果是在全局中使用require来加载模块,那么contextRequire为require
函数。如果在定义模块时,如定义一个"my/app",define(['dom','require'],function(dom,require){require(require('dojo/on')}),即使用局部加载器,那么contextRequire为"my/app"模块的require
函数,上下文是通过createContext
方法创建。 */ var module,syntheticMid; /* 1. a1 如果是一个模块mid字符串,则返回相对应的模块 2. a1 不是字符串,也不是数组(依赖或者要加载的模块列表。则为配置对像或者
函数。require({ aliases:[["text","dojo/text"]]}) 3. a1 第一个参数为数组,代表直接要加载的模块。 */ if(isString(a1)){ //
方法签名是一个模块模块标识符,用于检测模块是否在Modules /* getModule获得一个模块信息,第一个参数是模块 MID,第二个为引用模块(在模块标识符解析时,如果模块标识符为相对路径,那么先获得引用模块的路径,在计算相对路径)。 第三个参数是指,mid是否存在Modules,如果没有存在,则立即反回false,而不会把空上模块信息
添加到Modules. */ module = getModule(a1,true); if(module && module.executed){ return module.result; } throw makeError("undefinedModule",a1); } if(!isArray(a1)){ // a1 是一个配置对像,
调用上一节讲过的配置机制中的config
方法。 config(a1,referenceModule); // 处理参数,如果a2为请求加载的模块列表,a3为回调
函数,而a1经过上一步骤已经完成了配置,将 a2,a3的位置提前,方便下一步的处理; (a2,a3) may be (dependencies,callback) a1 = a2; a2 = a3; } if(isArray(a1)){ //
方法签名为 (requestList [,callback]) // 如果第一个参数为数组(1. require(['dojo/dom',function(){}],2. require({},'dojo/dom',function(){}),第二种情况会经过上一步处理参数。 if(!a1.length){ a2 && a2(); //如果数组为空数组,直接
调用回调
函数。 }else{ syntheticMid = "require*" + uid(); //每一个require都会有一个唯一标识. // 解析数组中的模块请求 for(var mid,deps = [],i = 0; i < a1.length;){ mid = a1[i++]; deps.push(getModule(mid,referenceModule)); //根据mid
获取一个模块,并
添加到依赖列表,如果是在define中定义的依赖,则referenceModule为定义的模块。referenceModule主要是提供相对路径 } // construct a synthetic module to control execution of the requestList,and,optionally,callback /* 构造一个人造模块(不是真实定义的模块),用于控制执行请求的模块链表(deps),及回调
函数。 可以这样理解,Dojo loader中都是以模块为操作对象,指定模块的依赖,工厂
函数,状态(注入,加载,执行)。 如果仅仅通过 require(['dojo/dom'],function(dom){}),而不构造一个人造模块。那么需要判断分别为require,define定义不同的加载依赖
链接的
方法。 makeModuleInfo:
方法签名(pid,mid,pack,url) return: {pid:pid,mid:mid,pack:pack,url:url,executed:0,def:0}; //executed: 模块未执行, def: 模块的工厂
函数 */ module = mix(makeModuleInfo("",syntheticMid,""),{ injected: arrived,//在模块中
添加角本注入状态 deps: deps,//将 require中的请求
链接,做为人造模块的依赖链表 def: a2 || noop,//如果有指定回调
函数,则工厂
函数为回调
函数,否则为空
函数 require: referenceModule ? referenceModule.require : req,// 指定加载器。如有指定了引用模块,那么
调用局部加载器。如果没有指定,
调用全局加载器 gc: 1 //garbage collect 垃级回收。在加载完依赖和执行完回调
函数后,销毁这个构造的模块。 }); //将构造的这个模块
添加到模块数组中.(modules 变量在上一节的配置机制里定义) modules[module.mid] = module; // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies /* 注入依赖列表。 */ injectDependencies(module); // try to immediately execute // if already traversing a factory tree,then strict causes circular dependency to abort the execution; maybe // it's possible to execute this require later after the current traversal completes and avoid the circular dependency. // ...but *always* insist on immediate in synch mode /* 尝试直接运行模块的工厂
函数。 strict 主要是在遍历工厂
函数时(checkComplete), 避免循环依赖.可以通过下面的例子来理解. require(['my/app'],function(app){ }) ** my/app.js define(['./app1'],function(app1){ require(['dojo/dom']) console.log('a') }) ** my/app1.js define(['require'],function(require){ require(["./app"]) }) 第一个require因为没有遍历到工厂
函数组成的树对像。即还没有
调用checkComplete(),所以checkCompleteGuard为0 第二个 require,即app1.js里面的require. 因为正在处于app1 模块的树对像,所以checkCompleteGuard 为 1. 可以查看 checkComplete(). checkComplete 会在guardCheckComplete中,先将checkCompleteGuard++,然后遍历所有的exeQ中的模块,并执行模块execModule(运行工厂
方法)。当执行到app1模块时,发现require(["./app"]). 那么就会
调用require
方法,构造一个人造模块require*_1 此时checkCompleteGuard为1, leagacyMode!=sync.所以 strict 为1. 在执行require*_1时,exeModule要先执行my/app模块的工厂
函数 */ var strict = checkCompleteGuard && legacyMode!=sync; guardCheckComplete(function(){ execModule(module,strict); }); if(!module.executed){ // some deps weren't on board or circular dependency detected and strict; therefore,push into the execQ execQ.push(module); } checkComplete(); } } return contextRequire; },createRequire = function(module){ if(!module){ return req; } var result = module.require; if(!result){ result = function(a1,a3){ return contextRequire(a1,module,result); }; module.require = mix(result,req); result.module = module; result.toUrl = function(name){ return toUrl(name,module); }; result.toAbsMid = function(mid){ return toAbsMid(mid,module); }; if(has("dojo-undef-api")){ result.undef = function(mid){ req.undef(mid,module); }; } if(has("dojo-sync-loader")){ result.syncLoadNls = function(mid){ var nlsModuleInfo = getModuleInfo(mid,module),nlsModule = modules[nlsModuleInfo.mid]; if(!nlsModule || !nlsModule.executed){ cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url]; if(cached){ evalModuleText(cached); nlsModule = modules[nlsModuleInfo.mid]; } } return nlsModule && nlsModule.executed && nlsModule.result; }; } } return result; },execQ = // The list of modules that need to be evaluated. [],defQ = // 每次
调用了define
函数后,都会把这个define
函数的参数,组成一个数组,
添加到defQ中,格式为:[mid,dependencs,facotry],然后在runDefQ中会弹出数组的元素。 [],waiting = // The set of modules upon which the loader is waiting for definition to arrive {},setRequested = function(module){ module.injected = requested; waiting[module.mid] = 1; if(module.url){ waiting[module.url] = module.pack || 1; } startTimer(); },setArrived = function(module){ module.injected = arrived; delete waiting[module.mid]; if(module.url){ delete waiting[module.url]; } if(isEmpty(waiting)){ clearTimer(); has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync); } },execComplete = req.idle = // says the loader has completed (or not) its work function(){ return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard; },runMapProg = function(targetMid,map){ // search for targetMid in map; return the map item if found; falsy otherwise if(map){ for(var i = 0; i < map.length; i++){ if(map[i][2].test(targetMid)){ return map[i]; } } } return 0; },compactPath = function(path){ /* 路径处理
函数,返回一个简洁的路径 传入的path可能为 MID,或者referenceModule+".." +MID; */ var result = [],segment,lastSegment; path = path.replace(/\\/g,'/').split('/'); // 将path中的反斜线,夫换为 linux下的路径形式 while(path.length){ /* 1. path 中包含 ".." 时
去除掉 紧靠 ".." 的路径 如 dojo/dom/../sniff 会变为dojo/sniff; 2. path 中包含 "." 时,直接跳过 */ segment = path.shift(); if(segment==".." && result.length && lastSegment!=".."){ result.pop(); lastSegment = result[result.length - 1]; }else if(segment!="."){ result.push(lastSegment= segment); } // else ignore "." } return result.join("/"); },makeModuleInfo = function(pid,url){ /*
方法签名: pid: String,包的标识符 mid: String,模块标识符 pack: Object,包的详细信息 { main="main",name="dojo",location="lib/dojo"} url: String,模块解析后的地址 return: Object {pid:pid,def:0}; //executed: 模块未执行, def: 模块的工厂
函数 */ if(has("dojo-sync-loader")){ var xd= req.isXdUrl(url); return {pid:pid,def:0,isXd:xd,isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))}; }else{ return {pid:pid,def:0}; //executed: 模块未执行, def: 模块的工厂
函数 } },getModuleInfo_ = function(mid,packs,modules,pathsMapProg,alwaysCreate){ // 模块标识符解析
函数,将一个字符解析为实际的路径。 // 通过传递参数来代替使用词法变量(查看词法作用域,例如,不是modules,不是直接访问loader的本地变量), 使得 getModuleInfo_ 可以被loader单独使用(例如, builder) // 在这种情况下 (builder) 会非常有用。 getModuleInfo 不会返回已以存在的模块引用, 而总是创建一个新的。 // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g.,the builder) // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader /* mid: String 模块Id标识符 referenceModule: Object 参考模块 packs: Dojo Config里面的packages包对象。 modules: 已经存在的模块, baseUrl: 根路径, mapProgs: 配置变量map或者packageMap,pathsMapProg: 配置变量paths,aliases 配置变量aliases的对象 alwaysCreate: 如果指定为true,一直创建新的文档,默认为undefined. */ /* 当指定了map对象时,packages:[ {name:"dojo16",location:"lib/dojo16"},{name:"dijit16",location:"lib/dijit16"},{name:"dojox16",location:"lib/dojox16"},{name:"dojo",location:"lib/dojo"},{name:"myOldApp",location:"myOldApp"},{name:"my",location:"my"} ],map:{ myOldApp:{ dojo:"dojo16",dijit:"dijit16",dojox:"dojox16" } }
输出如下,console.log(mapProgs) [["myOldApp",[["dijit","dijit16",RegExp /^dijit(\/|$)/,5],["dojox","dojox16",RegExp /^dojox(\/|$)/,["dojo","dojo16",RegExp /^dojo(\/|$)/,4]],8]] */ var pid,midInPackage,mapItem,url,result,isRelative,requestedMid; requestedMid = mid; isRelative = /^\./.test(mid); // 以 "." 开头的mid为相对路径,主要用于package. if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){ // absolute path or protocol of .js filetype,or relative path but no reference module and therefore relative to page // whatever it is,it's not a module but just a URL of some sort // note: pid===0 indicates the routine is returning an unmodified mid /*
绝对路径,以 "/"开头,或者是一种协议如http:,或者是以 .js 结尾的
文件类型, 或者是 相对路径( 以 ".")开头,而没有referenceModule. 这里请求的可能是任意
数量的javascript
代码,而不是一AMD规范的模块。 注意: 如果 pid===0 标志着程序会返回一个未
修改的mid. */ return makeModuleInfo(0,mid); }else{ // relative module ids are relative to the referenceModule; get rid of any dots 以"."开头的对头模块标识是相对于 referenceModule(解释参考术语名词). 而忽略其它的点。 mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid); if(/^\./.test(mid)){ // require['dojo/../.d/dom'] 不知道这个
函数是不是要处理这种极端的问题 throw makeError("irrationalPath",mid); } // at this point,mid is an absolute mid // map the mid /* !** 待
解决 */ if(referenceModule){ mapItem = runMapProg(referenceModule.mid,mapProgs); } mapItem = mapItem || mapProgs.star; mapItem = mapItem && runMapProg(mid,mapItem[1]); if(mapItem){ mid = mapItem[1] + mid.substring(mapItem[3]); } match = mid.match(/^([^\/]+)(\/(.+))?$/); pid = match ? match[1] : ""; if((pack = packs[pid])){ mid = pid + "/" + (midInPackage = (match[3] || pack.main)); }else{ pid = ""; } // search aliases var candidateLength = 0,candidate = 0; forEach(aliases,function(pair){ var match = mid.match(pair[0]); if(match && match.length>candidateLength){ candidate = isFunction(pair[1]) ? mid.replace(pair[0],pair[1]) : pair[1]; } }); if(candidate){ return getModuleInfo_(candidate,alwaysCreate); } result = modules[mid]; if(result){ return alwaysCreate ? makeModuleInfo(result.pid,result.mid,result.pack,result.url) : modules[mid]; } } // get here iff the sought-after module does not yet exist; therefore,we need to compute the URL given the // fully resolved (i.e.,all relative indicators and package mapping resolved) module id /* 此时 寻找加载的模块还没有存在,因些我们需要对过完全解析的模块id(如,完成所有的相对标识及包映射) 来计算它的URL */ // note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid 注意: 当 pid!==0时, 表明程序会返回未变的mid, 并在mid后
添加.js的URL mapItem = runMapProg(mid,pathsMapProg); // 配置变量中的paths. if(mapItem){ url = mapItem[1] + mid.substring(mapItem[3]); }else if(pid){ url = pack.location + "/" + midInPackage; }else if(has("config-tlmSiblingOfDojo")){ url = "../" + mid; }else{ url = mid; } // if result is not absolute,add baseUrl if(!(/(^\/)|(\:)/.test(url))){ url = baseUrl + url; } url += ".js"; return makeModuleInfo(pid,compactPath(url)); },getModuleInfo = function(mid,fromPendingCache){ return getModuleInfo_(mid,req.baseUrl,fromPendingCache ? [] : mapProgs,fromPendingCache ? [] : pathsMapProg,fromPendingCache ? [] : aliases); },resolvePluginResourceId = function(plugin,prid,referenceModule){ return plugin.normalize ? plugin.normalize(prid,function(mid){return toAbsMid(mid,referenceModule);}) : toAbsMid(prid,referenceModule); },dynamicPluginUidGenerator = 0,getModule = function(mid,immediate){ // compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule var match,plugin,result; match = mid.match(/^(.+?)\!(.*)$/); if(match){ // name was <plugin-module>!<plugin-resource-id> plugin = getModule(match[1],immediate); if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){ injectModule(plugin); if(plugin.injected===arrived && !plugin.executed){ guardCheckComplete(function(){ execModule(plugin); }); } if(plugin.executed){ promoteModuleToPlugin(plugin); }else{ // we are in xdomain mode for some reason execQ.unshift(plugin); } } if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } // if the plugin has not been loaded,then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise if(plugin.load){ prid = resolvePluginResourceId(plugin,match[2],referenceModule); mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid); }else{ prid = match[2]; mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin"; } result = {plugin:plugin,req:createRequire(referenceModule),prid:prid}; }else{ result = getModuleInfo(mid,referenceModule); } return modules[result.mid] || (!immediate && (modules[result.mid] = result)); },toAbsMid = req.toAbsMid = function(mid,referenceModule){ //将给定的mid, 通过模块标识符解析后,获得一个绝的模块标识符。 return getModuleInfo(mid,referenceModule).mid; },toUrl = req.toUrl = function(name,referenceModule){ /* 获得一个资源的路径 name: string, 由模块标识符作为前缀,如在dojo包内有一张
图片,地址为js/lib/dojo/main.jpg, 那么指定的
名称可以为 dojo/main.jpg. referenceModule: 哪个模块
调用了req.toUrl,如 my/app.js 里面
调用require.toUrl("./main.jpg"),那么 referenceModule为 my/app; */ var moduleInfo = getModuleInfo(name+"/x",referenceModule),url= moduleInfo.url; return fixupUrl(moduleInfo.pid===0 ? // if pid===0,then name had a protocol or absolute path; either way,toUrl is the identify function in such cases name : // "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id url.substring(0,url.length-5) ); },nonModuleProps = { injected: arrived,executed: executed,def: nonmodule,result: nonmodule },makeCjs = function(mid){ return modules[mid] = mix({mid:mid},nonModuleProps); },cjsRequireModule = makeCjs("require"),cjsExportsModule = makeCjs("exports"),cjsModuleModule = makeCjs("module"),runFactory = function(module,args){ req.trace("loader-run-factory",[module.mid]); var factory = module.def,result; has("dojo-sync-loader") && syncExecStack.unshift(module); if(has("config-dojo-loader-catches")){ try{ result= isFunction(factory) ? factory.apply(null,args) : factory; }catch(e){ signal(error,module.result = makeError("factoryThrew",[module,e])); } }else{ result= isFunction(factory) ? factory.apply(null,args) : factory; } module.result = result===undefined && module.cjs ? module.cjs.exports : result; has("dojo-sync-loader") && syncExecStack.shift(module); },abortExec = {},defOrder = 0,promoteModuleToPlugin = function(pluginModule){ var plugin = pluginModule.result; pluginModule.dynamic = plugin.dynamic; pluginModule.normalize = plugin.normalize; pluginModule.load = plugin.load; return pluginModule; },resolvePluginLoadQ = function(plugin){ // plugins is a newly executed module that has a loadQ waiting to run // step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid // recall the original mid was created before the plugin was on board and therefore it was impossible to // compute the final mid; accordingly,prid may or may not change,but the mid will definitely change var map = {}; forEach(plugin.loadQ,function(pseudoPluginResource){ // manufacture and insert the real module in modules var prid = resolvePluginResourceId(plugin,pseudoPluginResource.prid,pseudoPluginResource.req.module),mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/,prid) : (plugin.mid + "!" + prid),pluginResource = mix(mix({},pseudoPluginResource),{mid:mid,prid:prid,injected:0}); if(!modules[mid]){ // create a new (the real) plugin resource and inject it normally now that the plugin is on board injectPlugin(modules[mid] = pluginResource); } // else this was a duplicate request for the same (plugin,rid) for a nondynamic plugin // pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board) // mark is as arrived and delete it from modules; the real module was requested above map[pseudoPluginResource.mid] = modules[mid]; setArrived(pseudoPluginResource); delete modules[pseudoPluginResource.mid]; }); plugin.loadQ = 0; // step2: replace all references to any placeholder modules with real modules var substituteModules = function(module){ for(var replacement,deps = module.deps || [],i = 0; i<deps.length; i++){ replacement = map[deps[i].mid]; if(replacement){ deps[i] = replacement; } } }; for(var p in modules){ substituteModules(modules[p]); } forEach(execQ,substituteModules); },finishExec = function(module){ req.trace("loader-finish-exec",[module.mid]); module.executed = executed; module.defOrder = defOrder++; has("dojo-sync-loader") && forEach(module.provides,function(cb){ cb(); }); if(module.loadQ){ // the module was a plugin promoteModuleToPlugin(module); resolvePluginLoadQ(module); } // remove all occurrences of this module from the execQ for(i = 0; i < execQ.length;){ if(execQ[i] === module){ execQ.splice(i,1); }else{ i++; } } // delete references to synthetic modules if (/^require\*/.test(module.mid)) { delete modules[module.mid]; } },circleTrace = [],execModule = function(module,strict){ // run the dependency vector,then run the factory for module // 运行依赖向量,然后运行模块的工厂
函数 if(module.executed === executing){ req.trace("loader-circular-dependency",[circleTrace.concat(module.mid).join("->")]); return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports); } // at this point the module is either not executed or fully executed if(!module.executed){ /* 依赖的模块刚刚请求完成,即完成角本注入,module.injected == "requested" 还不是received. 所以 def == 0 */ if(!module.def){ return abortExec; //反回 {} } var mid = module.mid,argResult,args = [],i = 0; if(has("dojo-trace-api")){ circleTrace.push(mid); req.trace("loader-exec-module",["exec",circleTrace.length,mid]); } // for circular dependencies,assume the first module encountered was executed OK // modules that circularly depend on a module that has not run its factory will get // the pre-made cjs.exports===module.result. They can take a reference to this object and/or // add properties to it. When the module finally runs its factory,the factory can // read/write/replace this object. Notice that so long as the object isn't replaced,any // reference taken earlier while walking the deps list is still valid. module.executed = executing; while((arg = deps[i++])){ argResult = ((arg === cjsRequireModule) ? createRequire(module) : ((arg === cjsExportsModule) ? module.cjs.exports : ((arg === cjsModuleModule) ? module.cjs : execModule(arg,strict)))); if(argResult === abortExec){ module.executed = 0; req.trace("loader-exec-module",["abort",mid]); has("dojo-trace-api") && circleTrace.pop(); return abortExec; } args.push(argResult); } runFactory(module,args); finishExec(module); has("dojo-trace-api") && circleTrace.pop(); } // at this point the module is guaranteed fully executed return module.result; },checkCompleteGuard = 0,guardCheckComplete = function(proc){ try{ checkCompleteGuard++; proc(); }finally{ checkCompleteGuard--; } if(execComplete()){ signal("idle",[]); } },checkComplete = function(){ // keep going through the execQ as long as at least one factory is executed // plugins,recursion,cached modules all make for many execution path possibilities if(checkCompleteGuard){ return; } guardCheckComplete(function(){ checkDojoRequirePlugin(); for(var currentDefOrder,i = 0; i < execQ.length;){ currentDefOrder = defOrder; module = execQ[i]; execModule(module); if(currentDefOrder!=defOrder){ // defOrder was bumped one or more times indicating something was executed (note,this indicates // the execQ was modified,maybe a lot (for example a later module causes an earlier module to execute) checkDojoRequirePlugin(); i = 0; }else{ // nothing happened; check the next module in the exec queue i++; } } }); }; if(has("dojo-undef-api")){ req.undef = function(moduleId,referenceModule){ // In order to reload a module,it must be undefined (this routine) and then re-requested. 为了重新加载一个模块, 它必须是未定义的(当前程序), 然后在重新加载。 // This is useful for testing frameworks (at least). 至少在测试框架时很有用。 var module = getModule(moduleId,referenceModule); setArrived(module); mix(module,{def:0,injected:0,node:0}); }; } // 以下是
支持加载跨域的模块(
支持加载不同域名下的模块)。 if(has("dojo-inject-api")){ if(has("dojo-loader-eval-hint-url")===undefined){ has.add("dojo-loader-eval-hint-url",1); } var fixupUrl= function(url){ url += ""; // make sure url is a Javascript string (some paths may be a Java string) return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : ""); },injectPlugin = function( module ){ // injects the plugin module given by module; may have to inject the plugin itself var plugin = module.plugin; if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } var onLoad = function(def){ module.result = def; setArrived(module); finishExec(module); checkComplete(); }; if(plugin.load){ plugin.load(module.prid,module.req,onLoad); }else if(plugin.loadQ){ plugin.loadQ.push(module); }else{ // the unshift instead of push is important: we don't want plugins to execute as // dependencies of some other module because this may cause circles when the plugin // loadQ is run; also,generally,we want plugins to run early since they may load // several other modules and therefore can potentially unblock many modules plugin.loadQ = [module]; execQ.unshift(plugin); injectModule(plugin); } },// for IE,injecting a module may result in a recursive execution if the module is in the cache cached = 0,injectingModule = 0,//表示当前正在注入的模块对像, 即正在创建一个script元素,并把它添到insertPointSibling之前。
添加完后把injectingModule赋值回 0. injectingCachedModule = 0,evalModuleText = function(text,module){ // see def() for the injectingCachedModule bracket; it simply causes a short,safe circuit if(has("config-stripStrict")){ text = text.replace(/"use strict"/g,''); } injectingCachedModule = 1; if(has("config-dojo-loader-catches")){ try{ if(text===cached){ cached.call(null); }else{ req.eval(text,has("dojo-loader-eval-hint-url") ? module.url : module.mid); } }catch(e){ signal(error,makeError("evalModuleThrew",module)); } }else{ if(text===cached){ cached.call(null); }else{ req.eval(text,has("dojo-loader-eval-hint-url") ? module.url : module.mid); } } injectingCachedModule = 0; },injectModule = function(module){ // Inject the module. In the browser environment,this means appending a script element into // the document; in other environments,it means loading a file. // // If in synchronous mode,then get the module synchronously if it's not xdomainLoading. /* 注入模块。 在浏览器环境下, 它会在文档中
添加一个script 元素, 在其它环境下,它会直接加载一个
文件。 如果是同步模式下, 而没有指定跨域加载,则会以同步的
方法获得一个模块。 */ var mid = module.mid,url = module.url; if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){ return; } setRequested(module); //设置inject的状态为requested,并将模块
添加到waiting对像中。表明这个模块正在等到下载(从服务器请求这个模块
文件) // dojo-combo-api 已弃用 if(has("dojo-combo-api")){ var viaCombo = 0; if(module.plugin && module.plugin.isCombo){ // a combo plugin; therefore,must be handled by combo service // the prid should have already been converted to a URL (if
required by the plugin) during // the normalize process; in any event,there is no way for the loader to know how to // to the conversion; therefore the third argument is zero req.combo.add(module.plugin.mid,module.prid,req); viaCombo = 1; }else if(!module.plugin){ viaCombo = req.combo.add(0,module.mid,module.url,req); } if(viaCombo){ comboPending= 1; return; } } // 如果模块为
插件,比如dojo/text!my/app.html,getModule
方法会返回一个包含 plugin
属性的对像,了解更多,请查看getModule
方法。 if(module.plugin){ injectPlugin(module); return; } // else a normal module (not a plugin) // 模块在加载完成后的回调
函数。 var onLoadCallback = function(){ runDefQ(module); //运行模块的工厂
函数 if(module.injected !== arrived){ // the script that contained the module arrived and has been executed yet // nothing was added to the defQ (so it wasn't an AMD module) and the module // wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module); // therefore,it must not have been a module; adjust state accordingly if(has("dojo-enforceDefine")){ signal(error,makeError("noDefine",module)); return; } setArrived(module); mix(module,nonModuleProps); req.trace("loader-define-nonmodule",[module.url]); } if(has("dojo-sync-loader") && legacyMode){ // must call checkComplete even in for sync loader because we may be in xdomainLoading mode; // but,if xd loading,then don't call checkComplete until out of the current sync traversal // in order to preserve order of execution of the dojo.
required modules !syncExecStack.length && checkComplete(); }else{ checkComplete(); } }; /* cache 对像是的配置机制中定义的对像,用于缓存已加载的资源。可以查看cache变量声明时的注释。 */ cached = cache[mid] || cache[urlKeyPrefix + module.url]; if(cached){ req.trace("loader-inject",["cache",url]); evalModuleText(cached,module); onLoadCallback(); return; } if(has("dojo-sync-loader") && legacyMode){ if(module.isXd){ // switch to async mode temporarily; if current legacyMode!=sync,then is must be one of {legacyAsync,xd,false} legacyMode==sync && (legacyMode = xd); // fall through and load via script injection }else if(module.isAmd && legacyMode!=sync){ // fall through and load via script injection }else{ // mode may be sync,xd/legacyAsync,or async; module may be AMD or legacy; but module is always located on the same domain var xhrCallback = function(text){ if(legacyMode==sync){ // the top of syncExecStack gives the current synchronously executing module; the loader needs // to know this if it has to switch to async loading in the middle of evaluating a legacy module // this happens when a modules dojo.require's a module that must be loaded async because it's xdomain // (using unshift/shift because there is no back() methods for Javascript arrays) syncExecStack.unshift(module); evalModuleText(text,module); syncExecStack.shift(); // maybe the module was an AMD module runDefQ(module); // legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing if(!module.cjs){ setArrived(module); finishExec(module); } if(module.finish){ // while synchronously evaluating this module,dojo.require was applied referencing a module // that had to be loaded async; therefore,the loader stopped answering all dojo.require // requests so they could be answered completely in the correct sequence; module.finish gives // the list of dojo.requires that must be re-applied once all target modules are available; // make a synthetic module to execute the dojo.require's in the correct order // compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module // TODO: can we just leave the module.finish...what's it hurting? var finishMid = mid + "*finish",finish = module.finish; delete module.finish; def(finishMid,("dojo/require!" + finish.join(",")).replace(/\./g,"/")],function(dojo){ forEach(finish,function(mid){ dojo.require(mid); }); }); // unshift,not push,which causes the current traversal to be reattempted from the top execQ.unshift(getModule(finishMid)); } onLoadCallback(); }else{ text = transformToAmd(module,text); if(text){ evalModuleText(text,module); onLoadCallback(); }else{ // if transformToAmd returned falsy,then the module was already AMD and it can be script-injected // do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache) injectingModule = module; req.injectUrl(fixupUrl(url),module); injectingModule = 0; } } }; req.trace("loader-inject",["xhr",legacyMode!=sync]); if(has("config-dojo-loader-catches")){ try{ req.getText(url,legacyMode!=sync,xhrCallback); }catch(e){ signal(error,makeError("xhrInject
Failed",e])); } }else{ req.getText(url,xhrCallback); } return; } } // else async mode or fell through in xdomain loading mode; either way,load by script injection req.trace("loader-inject",["script",url]); injectingModule = module; req.injectUrl(fixupUrl(url),module); injectingModule = 0; },defineModule = function(module,deps,def){ /* 定义一个模块 1. 跟def
函数的区别:def
函数即全局define
函数。只是构造一个数组[mid,dependences,factory],并
添加到defQ中。 2. 跟getModule的区别, getModule是获得一个对像 {pid="dojo",mid="dojo/dom",pack={...},more...} 并把这个对像存入到 modules对像中, 而没有依赖deps,工厂
函数def。 */ req.trace("loader-define-module",[module.mid,deps]); if(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){ // the module is a plugin resource loaded by the combo service // note: check for module.plugin should be enough since normal plugin resources should // not follow this path; module.plugin.isCombo is future-proofing belt and suspenders module.result = isFunction(def) ? def() : def; setArrived(module); finishExec(module); return module; } var mid = module.mid; if(module.injected === arrived){ signal(error,makeError("multipleDefine",module)); return module; } mix(module,{ deps: deps,def: def,cjs: { //CommonJS id: module.mid,uri: module.url,exports: (module.result = {}),setExports: function(exports){ module.cjs.exports = exports; },config:function(){ return module.config; } } }); // resolve deps with respect to this module for(var i = 0; deps[i]; i++){ deps[i] = getModule(deps[i],module); } // 可以忽略 if(has("dojo-sync-loader") && legacyMode && !waiting[mid]){ // the module showed up without being asked for; it was probably in a <script> element injectDependencies(module); execQ.push(module); checkComplete(); } //设置模块为到达状态 setArrived(module); if(!isFunction(def) && !deps.length){ module.result = def; finishExec(module); } return module; },runDefQ = function(referenceModule,mids){ // defQ is an array of [id,factory] // mids (if any) is a vector of mids given by a combo service var definedModules = [],args; /* defQ: 是每次
调用了define
函数后,都会把这个define
函数的参数,组成一个数组,
添加到defQ中,格式为:[mid,dependenceies,factory] defQ.length 为 1, 因为每次运行define之后,会把[mid,factory]
添加到defQ中,length为 1。 之后
调用injectModule的 onLoadCallback中
调用 runDefQ(模块对像). defQ.shift()之后,defQ变为空数组。 */ while(defQ.length){ args = defQ.shift(); mids && (args[0]= mids.shift()); // explicit define indicates possible multiple modules in a single file; delay injecting dependencies until defQ fully // processed since modules earlier in the queue depend on already-arrived modules that are later in the queue // TODO: what if no args[0] and no referenceModule /* 如果在define 的第一个参数没有指定为字符串(模块名),那么就使用 injectModule中传入的模块对像。 */ module = (args[0] && getModule(args[0])) || referenceModule; definedModules.push([module,args[1],args[2]]); } // 可以忽略,因为只在配置中指定了cache才有效。 consumePendingCacheInsert(referenceModule); forEach(definedModules,function(args){ /* 先定义这个模块。defindModule会给 referenceModule对像
添加 deps,def两个
属性。并返回模块对像。之后通过injectDependencies加载依赖 */ injectDependencies(defineModule.apply(null,args)); }); }; } var timerId = 0,clearTimer = noop,startTimer = noop; if(has("dojo-timeout-api")){ // Timer machinery that monitors how long the loader is waiting and signals an error when the timer runs out. // 配合 waitSeconds 配置,提交一个模块请求后,触发一个setTimeout,如果在这个时间内没有加载完模块,并
调用clearTimer,则触发一个加载
错误。 clearTimer = function(){ timerId && clearTimeout(timerId); timerId = 0; }; startTimer = function(){ clearTimer(); if(req.waitms){ timerId = window.setTimeout(function(){ clearTimer(); signal(error,makeError("timeout",waiting)); },req.waitms); } }; } if (has("dom")) { // Test for IE's different way of signaling when scripts finish loading. Note that according to // http://bugs.dojotoolkit.org/ticket/15096#comment:14,IE9 also needs to follow the // IE specific code path even though it has an addEventListener() method. // Unknown if special path needed on IE10+,which also has a document.attachEvent() method. // Should evaluate to false for Opera and Windows 8 apps,even though they document.attachEvent() // is defined in both those environments. has.add("ie-event-behavior",doc.attachEvent && typeof Windows === "undefined" && (typeof opera === "undefined" || opera.toString() != "[object Opera]")); } if(has("dom") && (has("dojo-inject-api") || has("dojo-dom-ready-api"))){ var domOn = function(node,eventName,ieEventName,handler){ // Add an event listener to a DOM node using the API appropriate for the current browser; // return a function that will disconnect the listener. if(!has("ie-event-behavior")){ node.addEventListener(eventName,handler,false); return function(){ node.removeEventListener(eventName,false); }; }else{ node.attachEvent(ieEventName,handler); return function(){ node.detachEvent(ieEventName,handler); }; } },windowOnLoadListener = domOn(window,"load","onload",function(){ req.pageLoaded = 1; doc.readyState!="complete" && (doc.readyState = "complete"); windowOnLoadListener(); }); if(has("dojo-inject-api")){ // if the loader is on the page,there must be at least one script element // getting its parent and then doing insertBefore solves the "Operation Aborted" // error in IE from appending to a node that isn't properly closed; see // dojo/tests/_base/loader/requirejs/simple-badbase.html for an example // don't use scripts with type dojo/... since these may be removed; see #15809 // prefer to use the insertPoint computed during the config sniff in case a script is removed; see #16958 var scripts = doc.getElementsByTagName("script"),script; while(!insertPointSibling){ if(!/^dojo/.test((script = scripts[i++]) && script.type)){ insertPointSibling= script; } } req.injectUrl = function(url,owner){ /* 插入script元素到insert-point之前。 script的src的
属性值为 url; 加载完成后
调用callback. ower的值为模块对像。 */ var node = owner.node = doc.createElement("script"),onLoad = function(e){ e = e || window.event; var node = e.target || e.srcElement; if(e.type === "load" || /complete|loaded/.test(node.readyState)){ loadDisconnector(); //注销load监听器 errorDisconnector(); //注销error监听器 callback && callback(); } },loadDisconnector = domOn(node,"onreadystatechange",onLoad),//
添加一个监听器,并返回一个注销监听器的
方法 errorDisconnector = domOn(node,"error","onerror",function(e){ loadDisconnector(); errorDisconnector(); signal(error,makeError("scriptError",[url,e])); }); node.type = "text/javascript"; node.charset = "utf-8"; node.src = url; insertPointSibling.parentNode.insertBefore(node,insertPointSibling); return node; }; } } if(has("dojo-log-api")){ req.log = function(){ // 等于console.log,每一个被传递的值会被单独到
输出到一行里。 try{ for(var i = 0; i < arguments.length; i++){ console.log(arguments[i]); } }catch(e){} }; }else{ req.log = noop; } if(has("dojo-trace-api")){ var trace = req.trace = function( group,// the trace group to which this application belongs args // the contents of the trace ){ /// // Tracing interface by group. // // Sends the contents of args to the console iff (req.trace.on && req.trace[group]) if(trace.on && trace.group[group]){ signal("trace",[group,args]); for(var arg,dump = [],text= "trace:" + group + (args.length ? (":" + args[0]) : ""),i= 1; i<args.length;){ arg = args[i++]; if(isString(arg)){ text += "," + arg; }else{ dump.push(arg); } } req.log(text); dump.length && dump.push("."); req.log.apply(req,dump); } }; mix(trace,{ on:1,group:{},set:function(group,value){ if(isString(group)){ trace.group[group]= value; }else{ mix(trace.group,group); } } }); trace.set(mix(mix(mix({},defaultConfig.trace),userConfig.trace),dojoSniffConfig.trace)); on("config",function(config){ config.trace && trace.set(config.trace); }); }else{ req.trace = noop; } var def = function( mid,//(commonjs.moduleId,optional) dependencies,optional) list of modules to be loaded before running factory factory //(any) ){ /// // Advises the loader of a module factory. //Implements http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition. /// //note // CommonJS factory scan courtesy of http://requirejs.org var arity = arguments.length,defaultDeps = ["require","exports","module"],// the predominate signature... args = [0,dependencies]; if(arity==1){ args = [0,(isFunction(mid) ? defaultDeps : []),mid]; }else if(arity==2 && isString(mid)){ args = [mid,(isFunction(dependencies) ? defaultDeps : []),dependencies]; }else if(arity==3){ args = [mid,factory]; } if(has("dojo-amd-factory-scan") && args[1]===defaultDeps){ args[2].toString() .replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,"") .replace(/require\(["']([\w\!\-_\.\/]+)["']\)/g,function(match,dep){ args[1].push(dep); // 如果是AMD 工厂
函数,默认加入 require,exports,module. }); } req.trace("loader-define",args.slice(0,2)); var targetModule = args[0] && getModule(args[0]),module; if(targetModule && !waiting[targetModule.mid]){ // given a mid that hasn't been requested; therefore,defined through means other than injecting // consequent to a require() or define() application; examples include defining modules on-the-fly // due to some code path or including a module in a script element. In any case,// there is no callback waiting to finish processing and nothing to trigger the defQ and the // dependencies are never requested; therefore,do it here. injectDependencies(defineModule(targetModule,args[2])); }else if(!has("ie-event-behavior") || !has("host-browser") || injectingCachedModule){ // not IE path: anonymous module and therefore must have been injected; therefore,onLoad will fire immediately // after script finishes being evaluated and the defQ can be run from that callback to detect the module id defQ.push(args); }else{ // IE path: possibly anonymous module and therefore injected; therefore,cannot depend on 1-to-1,// in-order exec of onLoad with script eval (since it's IE) and must manually detect here targetModule = targetModule || injectingModule; if(!targetModule){ for(mid in waiting){ module = modules[mid]; if(module && module.node && module.node.readyState === 'interactive'){ targetModule = module; break; } } if(has("dojo-combo-api") && !targetModule){ for(var i = 0; i<combosPending.length; i++){ targetModule = combosPending[i]; if(targetModule.node && targetModule.node.readyState === 'interactive'){ break; } targetModule= 0; } } } if(has("dojo-combo-api") && isArray(targetModule)){ injectDependencies(defineModule(getModule(targetModule.shift()),args[2])); if(!targetModule.length){ combosPending.splice(i,1); } }else if(targetModule){ consumePendingCacheInsert(targetModule); injectDependencies(defineModule(targetModule,args[2])); }else{ signal(error,makeError("ieDefine
Failed",args[0])); } checkComplete(); } }; def.amd = { vendor:"dojotoolkit.org" }; if(has("dojo-requirejs-api")){ req.def = def; } // allow config to override default implementation of named functions; this is useful for // non-browser environments,e.g.,overriding injectUrl,getText,log,etc. in node.js,Rhino,etc. // also useful for testing and monkey patching loader mix(mix(req,defaultConfig.loaderPatch),userConfig.loaderPatch); // now that req is fully initialized and won't change,we can hook it up to the error signal on(error,function(arg){ try{ console.error(arg); if(arg instanceof Error){ for(var p in arg){ console.log(p + ":",arg[p]); } console.log("."); } }catch(e){} }); // always publish these mix(req,{ uid:uid,cache:cache,packs:packs }); //
禁止loader向外提供更多信息,build时会设置为0 if(has("dojo-publish-privates")){ mix(req,{ // these may be interesting to look at when debugging paths:paths,aliases:aliases,legacyMode:legacyMode,defQ:defQ,waiting:waiting,// these are used for testing // TODO: move testing infrastructure to a different has feature packs:packs,mapProgs:mapProgs,pathsMapProg:pathsMapProg,listenerQueues:listenerQueues,// these are used by the builder (at least) computeMapProg:computeMapProg,computeAliases:computeAliases,runMapProg:runMapProg,compactPath:compactPath,getModuleInfo:getModuleInfo_ }); } // the loader can be defined exactly once; look for global define which is the symbol AMD loaders are // *
required* to define (as opposed to require,which is optional) if(global.define){ if(has("dojo-log-api")){ signal(error,makeError("defineAlreadyDefined",0)); } return; }else{ global.define = def; global.require = req; if(has("host-node")){ require = req; } } if(has("dojo-combo-api") && req.combo && req.combo.plugins){ var plugins = req.combo.plugins,pluginName; for(pluginName in plugins){ mix(mix(getModule(pluginName),plugins[pluginName]),{isCombo:1,executed:"executed",load:1}); } } if(has("dojo-config-api")){ forEach(delayedModuleConfig,function(c){ config(c); }); var bootDeps = dojoSniffConfig.deps || userConfig.deps || defaultConfig.deps,bootCallback = dojoSniffConfig.callback || userConfig.callback || defaultConfig.callback; req.boot = (bootDeps || bootCallback) ? [bootDeps || [],bootCallback] : 0; } if(!has("dojo-built")){ !req.async && req(["dojo"]); req.boot && req.apply(null,req.boot); } }) //>>excludeStart("replaceLoaderConfig",kwArgs.replaceLoaderConfig); ( // userConfig (function(){ // make sure we're looking at global dojoConfig etc. return this.dojoConfig || this.djConfig || this.require || {}; })(),// defaultConfig { // the default configuration for a browser; this will be modified by other environments hasCache:{ "host-browser":1,"dom":1,"dojo-amd-factory-scan":1,"dojo-loader":1,"dojo-has-api":1,"dojo-inject-api":1,"dojo-timeout-api":1,"dojo-trace-api":1,"dojo-log-api":1,"dojo-dom-ready-api":1,"dojo-publish-privates":1,"dojo-config-api":1,"dojo-sniff":1,"dojo-sync-loader":1,"dojo-test-sniff":1,"config-deferredInstrumentation":1,"config-useDeferredInstrumentation":"report-unhandled-rejections","config-tlmSiblingOfDojo":1 },packages:[{ // note: like v1.6-,this bootstrap computes baseUrl to be the dojo directory name:'dojo',location:'.' },{ name:'tests',location:'./tests' },{ name:'dijit',location:'../dijit' },{ name:'build',location:'../util/build' },{ name:'doh',location:'../util/doh' },{ name:'dojox',location:'../dojox' },{ name:'demos',location:'../demos' }],trace:{ // 在加载一个模块时,需要跟踪哪些信息,先设置以下的项,在
调用require.trace.on "loader-inject":0,//
输出模块加入到应用程序时的信息 "loader-define":0,"loader-exec-module":0,"loader-run-factory":0,"loader-finish-exec":0,"loader-define-module":0,"loader-circular-dependency":0,"loader-define-nonmodule":0 },async:0,waitSeconds:15 } ); //>>excludeEnd("replaceLoaderConfig")