dojo 加载器源文件讲解

之前写的都是Dojo的基本使用教程,整体的讲解了Dojo  AMD加载器使用及原理, DOM 操作, 事件,动画,这些都是平常工作中会经常用到的, 但对于真正的提升还是要分析源代码,了解整个的框架设计,同时针对每个函数或每行代码,看看大师们是怎么写的,有助于自己提写自己的编码水平。 

我们先从 dojo 包中 dojo.js 开始讲解。

概述

 dojo.js 这个文件是 dojo 的加载器,实现模块的定义与加载,主要暴露两个函数 define 和 require, 关于他的使用,在讲解源代码之前,你最好先了解 AMD模块定义, Dojo 加载器高级使用, 及 dojo 加载器参考文档.

加载器的源代码由以下几个部分组成, 也是整个加载器的开发路线图,之后主要依据这个路线图来做开发:
  1.  整个Dojo的初始化
  2. 小型库代码,提供基础的公用函数
  3. 定义has.js API, loader从头到尾有很多特征检测,都需要使用到这个API
  4. 定义 nodejs 和 rhino 侦测
  5. 定义 加载器使用到的数据
  6. 定义配置管理机制,如何将对userConfig, defaultConfig 以及其它的配置进行管理
  7. 定义对script元素的嗅探机制(CDN), 及以script 角本中的 data-dojoj-config中的数据嗅探。
  8. 根据用户提供的配置, defaultConfig, 以及第6步嗅探到的数据, 然后通过第5步的配置机制,来对loader进行整体的配置。
  9. 定义全局的 require 函数
  10. 定义模块解析机制, 如何根据模块id,解析为实际路径, 运行模块的工厂函数。
  11. 模块和插件模块定义机制( define 函数)
  12. 定义角本注入机制
  13. 定义 window加载侦测(dom, domReady)
  14. 定义日志API(功能)
  15. 定义跟踪API.
  16. 实现AMD规范中需要定义的函数
  17. 定义 dojo v1.x provide/require的机制,实现兼容之前版本的模块定义和请求
  18. 发布全局变量

初始化

初始函数是一个匿名闭包函数,接受两个参数,一个是用户指定的配置userConfig,  一个用默认的配置。  以下是提取出来的代码,最重要的是注释。

(function(userConfig, defaultConfig){

    
        //主体代码

})(
     // 传入用户配置
        function(){
            return this.dojoConfig || this.djConfig || this.require || {};
        },
    //传入默认配置
    {
        // 传入浏览器环境参数(通过has.js特征检测确定运行环境), 在其它环境,如nodejs时,会修改这些配置, 大部分选项都是用于build做优化的,这里有详细介绍has选项,http://dojotoolkit.org/reference-guide/1.9/dojo/has.html
        hasCache:{
            "host-browser": 1, //浏览器环境下使用dojo
            "dom":1,  //支持浏览器文档对像
            "dojo-amd-factory-scan":1, // loader选项,不是has.add 特征检测,默认情况是CommonJs的模块请求 define(["require","export","module",function(require,export,module){}), 如果指定为true,使用AMD扫描factory, 会自动设置这三个模块。即直接定义 define([],function(){})
            "dojo-loader":1, //表明使用dojo loader, 而不是requirejs等三方的。在_base/loader.js会使用它个选项来判断加载器是不是dojo 本身加载器,如果是第三方的,则无法加载老版本的Dojo.
            "dojo-has-api":1, //应用在has.js里, 主用于 has.js 里检测是否之前已经定义了has, has.add方法,如果使用了  dojo-loader,那么loader已经定义了这两个方法,在has.js里无需在定义这两个方法。如果使用第三方的加载器,则 dojo-has-api 默认为没有定义,那么在has.js里会重新定义
            "dojo-inject-api":1, // 应用在dojo.js, 表示是否支持跨域加载模块。
            "dojo-timeout-api":1, // 应用在dojo.js, 配合waitSeconds, 提供一个加载超时的API. 如是不设这个为true, 那么waitSeconds 也是无效的,因为没有一个计时器的功能。
            "dojo-log-api":1, // dojo.js,  主要用于提供日志功能,req.log('a','b'), 会调用console.log函数,并且把每个参数按行来输出。arguments[0], arguments[1]。
            "dojo-dom-ready-api":1, //dojo.js 提供一个监听器,当dom加载完成时,会通知这个监听器。
            "dojo-publish-privates":1, // dojo.js, 是否允许公开loader 的私有方法或者变量。 build时设置为 0
            "dojo-config-api":1, //dojo.js 确保在 dojo在 build时可配置
            "dojo-sniff":1, //dojo.js,允许扫描dojo.js 角本中的 data-dojo-config 和 djConfig 标签
            "dojo-sync-loader":1, // dojo.js ,是否可以使用老版本的加载器
            "dojo-test-sniff":1, // dojo.js, 是否需要侦测 Doh(单元测试)模块的测试配置 testConfig, build时为0
            "config-deferredInstrumentation":1, // deferred.js, 加载dojo/promise/instrumentation模块. 该模块用于监测被拒绝的承诺,将末被处理的错误输出到控制台. build 设置为0
            "config-useDeferredInstrumentation":"report-unhandled-rejections", // dojo/promise/instrumentation.js, 只将末被处理的拒绝,输出到控制台。 如果为report-rejections, 输出所有被拒绝的Promise 的信息
            "config-tlmSiblingOfDojo":1 // dojo.js ,是否允许运行非标准化的模块名解析, 模块名解析就是根据字符串,解析为正确的模块路径

        },
        packages:[{
            // 如果在dojoConfig中没有设置baseURL, 引导程序会计算baseUrl的路径为dojo文件夹,即包含 dojo.js的文夹件
            name:'dojo',  // 例如, 加载 dojo/dom, dojo代表包名,包的地址为baseUrl的文件夹,模块的地址为 baserUrl/dom.js
            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, //运行模块的factory时的信息
            "loader-finish-exec":0,
            "loader-define-module":0,
            "loader-circular-dependency":0,
            "loader-define-nonmodule":0
        },
        asycn:0,  //默认为0, 以同步的方法加载,而不是AMD,这种加载会先把所有的基础模块如dojo/_base/kernel. 所以在用户配置里最好设置asycn:1. 这样就能按需加载。
        waitSeconds:15  //加载一个模块的时间为15 秒,如果模块没有在规定时间内加载完成,触发加载错误,这样Dojo就能知道如何处理,而不是一直等待


    }

)

小型库

  • noop -  空函数, 主要有三种应用,第一种需要初始化一个变量,传入一个noop引用(推荐使用,可以节省内存和代码),或者直接在变量后传入 funtion(){};  相当于声明一个函数娈量,这样在任何时该都能调用这个变量(空函数调用什么也不做), 满足某条件时在定义具体的功能(传入具体的函数)。如 var a=noop; a();  a=function(a){console.log('a')}; a('hello');   第二种是删除一个变量的功能,如 var a = function(){setTimeout(function(){console.log('a')}, 1000)};  a();  当你不想要这个计时器器功能时,可以 a=noop.   之后使用时还是可以调用 a函数,但不做任何时,如果 a=null. 你就不能把a作为一个函数来调用了。 第三种, 做为函数的传递值,比如回调函数中。你又没有具体要做的功能,你可以传递noop.
  • isEmpty - 检测一个对像是否为空
  • toString - 调用对像的toString方法。 用于输出一个对像的字符串值
  • isFunction - 检测一个对像是否为函数
  • isString - 检测一个对像是否为字符串
  • isArray - 检测一个对像是否为数组
  • forEach - 枚举对像可枚举的值,并对每个值进行操作
  • mix - 混合, 将一个对像的所有属性,复制到另一个对像。
  • makeError - 自定义一个error对像
  • uidSeed 和 uid -  每次调用require时,会得一个唯一标识符,格式为 _ 数字,如 require(config), uidSeed 为:_1 在调用require(['dom'], function(dom){}); uidSeed为: _2
  • req - 全局的require函数,这个函数本来应该定义在第9条 contextRequire。但接下来的has API需要使用到req函数, 将has方法,添加到req上, 即 require.has().
  • global - 引用当前环境的全局对像
  • document - 引用当前环境的文档对像
  • element - 通过 document 创建一个节点元素.

 // 实现一个内部会使用到的小型库, 提供基础的方法
         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

has API

has API 主要是为 loader 提供了特征检测,当某个特征通过时,添加相应的代码,当某个特征末通过时删除相应的代码。 如果是采用第三方的 加载器,dojo toolkit会提供dojo/has.js 模块,并实现has.js 约定中的方法。 has API主要有以下几个变量和方法。

global -  引用当前的环境,作为测试函数的第一个参数传递

doc - 当前文档对像, 作为测试函数的第二个参数传递

element - 创建一个元素,作为测试函数的第三个参数传递

has -  方法, 作为特征检测主要的方法

has.add - 主法,添加一个特征,及测试。 并返回一个测试的值或者函数

       global= this,

             doc = global.document,

             element = doc && doc.createElement("Div"),

             has = req.has = function(name){  // 添加 has方法, 并将方法附加到 require 上。 内部可以直接通过 has(" feature name" )  外部可以通过 require.has(" feature Name")
                 return isFunction(hasCache[name]) ? hasCache[name] = hasCache[name](global, doc, element) : hasCache[name];  // 如果hasCach缓存的是一个测试函数,则调用这个测试函数,并将结果hasCache对应的项,如果不是,则直接返回相应的值
             },

             hasCache = has.cache = defaultConfig.hasCache;

    has.add = function(name, test, now,force){
       (hasCache[name] === undefined || force) && (hasCache[name] = test);   //整个表达式是的意思是: 如果没有定义name的特征或者强制覆盖,则进行赋值。 第一个括号用于两个表达式的共同的结果。
        return now && has(name); //当指定了now时,立即运行特侦测试,并返回结果。
    };


检测服务器环境

因为dojo加载器是支持 nodejs 和 rhino等服务器端的js 加载。所以先检测服务器特侦, 如果是相对于服务器环境,加载相应的配置文件,如果是对于前端开发, 可以跳过,或直接把这段给删除掉.

在检测了loader的使用环境后(浏览器, nodejs 或 rhino) . 需要将 userConfig中的 has测试 替换 defaultConfig的测试

    /*
     用于检测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);
    }
    
        //loader使用环境后,用 userConfig中的has 替换defaultConfig指定的has测试
    for(var p in userConfig.has){
        has.add(p, userConfig.has[p], 0, 1); //强制替换
    }

定义加载器数据

loader 不仅需要提供require, define 和 has API等功能,还需要有数据或者变量来标识当前loader的工作状态及其它属性.  这些属性主要分成两部分,一是标识模块的加载状态,二为loader 的工作模式(同步,AMD模式,传统的跨域模式等,大部分是定义同步会使用到的变量,而我们可以仅考虑AMD加载, 即 async: 1 时的情境, 所以不必深究这段代码, 可以跳过,直接到loader的事件API),如下所示:

var requested = 1,  //请求已经发出的状态
        arrived = 2,    //请求的模块已经到达
        nonmodule = 3, //请求的不是一个模块
        executing = 4,   // 模块正在执行
        executed =5; // 模块执行完成

    if(has("dojo-trace-api")){
        // 如果在 hasCache有指定dojo-trace-api(用于调试追踪模块的加载情况, 输出相应的信息), 则把数字替换为更友好的文字
        requested = "requested";
        arrived = "arrived";
        nonmodule = "not-a-module";
        executing = "executing";
        executed = "executed";
    }
//  使用同步模式加载的代码, 这段代码可以不用理会
    var legacyMode = 0,
        sync = "sync",
        xd = "xd",
        syncExecStack = [],
        dojoRequirePlugin = [],
        checkDojoRequirePlugin = noop,
        transformToAmd = noop,
        getXhr;

    if(has("dojo-sync-loader")){
        req.isXdUrl = noop;

        req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){
            // dojo/_base/loader.js 加载后会调用这个方法,来初始化同步加载器
            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")){
            // 如果采用传统的同步加载器方式, loader需要定义一个小型的Xhr 库。

            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("xhrFailed", xhr.status);
                }
                return xhr.responseText;
            };
        }  // 结束对同步或者跨域加载的数据或才方法定义
    }else{
        req.async = 1;
    }


eval 方法

定义的eval 方法主要有两个用途,一是执行 script 侦测到的的 data-dojo-config 或 djConfig 中的数据 如 data-dojo-config="async:1",   即字符串转化为一个对像,并赋值给你一个对像。 二 是用于同步模块加载过来的代码,因为 Ajax加载过来的代码返回的都是文本,需要调用eval方法来执行相应的模块代码。

  var eval_ = new Function('return eval(argument[0]);'); // 使用构造函数, 这样我们的 eval_ 函数就能形成一个闭包,不会污染全局作用域

    req.eval = function(text,hint){
        return eval_(text + "\r\n@ sourceURL=" + hint); 
    }


loader 内部事件API


内部事件API 主要用于报告错误, 配置发生改变,调试跟踪以及反馈当前的空前状态, 详细的使用可以参考 loader 文档

事件API需要实现以下功能:
  1. 需要有一个listenerQueues,用来保存所有事件类型及监听器, 格式如 {"error": [listen1, listen2, listen3]}
  2. on - 用于添加监听器,即把  on('error', handler) 添加到listenerQueues, 同时返回一个对像用于删除监听器。
  3. signal - 触发某个事件,从listenerQueres中调出想应的监听器。 
源代码及注释如下:

 // loader 事件 API

    var listenerQueues = {}, //监听器队列,用于保存所有的事件类型及对应的监听器
        error = "error",//因为错误类型的事件会在loader多次监听或者触发,所以定义一个error变量,可以直接在on(error, function(){}), 而不需要每次 on("error",function(){})
        on = req.on = function(type,listener){
            var queue = listenerQueues[type] || (listenerQueues[type]=[]); //如果没有存在相应的事件类型,则创建一个事件队列,并赋值。
            queue.push(listener);

            // 返回一个对像,用于删除监听器, 如  a=on('error',handler), 可以调用 a.remove() 删除listenerQueues中 error队列中的 handler 监听器。
            return{
                remove: function(){  //闭包函数,可以引用外围函数中的变量及参数
                    for(var i=0;i

实现配置机制

为什么要实现配置机制呢? 通过配置机制,我们可以通过 defaultConfig, userConfig , 编译时配置,向加载器提供数据,规定相应的行为,如提供包的配置, 别名配置,然后加载器可以根据相应的配置来解析模块标识符到正确的路径。

配置选项

需要先定义部份的变量,来存放配置数据。详细如下,具体的描述可以参考 loader 文档

  • aliases -数组,  根据别名可以映射到真实的模块, aliases是一个数组,它的值是 [regexs or string, replacement] 形式的数组(alias, actual), alias可以是字符串,也可以是正则表达式。
  • paths -{}, 遵循CommonJs paths路径格式, 映射模块标识的部分,到paths中指定的值。 如{"a/b": "myApp/ core/widget"}, 当require(['a/b/alert']) 时,会调用 myApp/core/widget/alert.js
  • pathsMapProg - 数组, 将 paths中的值转化为一个路径的映身数组,易于检索和匹配路径,格式为:(from-path, to-path, regex, length),  length为"a/b"的长度, 结果如: [["a/b","myApp/ core/widget",RegExp /^a\/b(\/|$)/,3]], 详细的处理函数可以查看 computeMapProg
  • packs - 从packageId 到 包配置(packages) 的一个对像, 看可查看处理函数 fixupPackageInfo 了解底层处理.
  • map - {}, AMD Map配置变量,因为dojo/_base/kernel 需要引用这个对像,所以通过 req.map 导出这个对像。map:{ myOldApp:{dojo:"dojo16"}}
  • mapProgs - 如同对paths的处理, mapProgs 是 computeMapProg运行map后得到的一个数组。举上面那个map值, map:{myOldApp:{dojo:"dojo16"}, 这里有定义三个包 myOldApp, dojo, dojo16。 map的作用是将myOldApp中的 dojo 包标识,映像到dojo16,而不是dojo. mapProgs的值是一个数组,而不是对像,在检测匹配时会更加高效。 可以查看computeMapProg. mapProgs的值可以如下:[["myOldApp",[["dijit","dijit16",RegExp /^dijit(\/|$)/,5],["dojox","dojox16",RegExp /^dojox(\/|$)/,5],["dojo","dojo16",RegExp /^dojo(\/|$)/,4]],RegExp /^myOldApp(\/|$)/,8]]
  • modules - {}, 值为 (mid) -> module object. 模块对像的命名如下所示:
  • pid:  模块所属包对像的标识符。(如" dojo");  "" 标识系统或者默认的包
  • mid: 全面解析后的模块标识符(如已经使用了包映像和路径映身,mapProgs, pathsMapProg) , 但还没有加上包标识符(如:"dojo/io"script")
  • url: 从哪检索模块的路径
  • pack:  详细的包信息,这里是一个包对像,如Object { main="main"name="dojo"location="lib/dojo"}
  • executed: 0 表示没有执行 executing => 正在遍历依赖和运行模块的工厂函数; executed => 工厂函数运行完成
  • deps: 模块的依赖向量(向量可以理解为javascript 的数组,但是有先后顺序)
  • def : 模块的工厂函数
  • result: 运行完工厂函数返回的结果值
  • injected: (0 | requested | arrived ) , nonmodule 表示资源没有调用 define函数
  • load: 插件加载函数;仅能用于插件
  • modules 需要通过以下步骤才能创建
    1. Requested: 其它模块定义时或require 在依赖向量里包含了请求的模块,或者在运行代码里有明确通过 req.require 请求这个模块。
    2. Injected:  被请求资源的URL 构成一个 script元素,当这个script被添加到 插入点元素时。
    3. Loaded:  第【2】步注入的资源被加载完。
    4. Defined:  资源包含 define语句时,则告诉loader 这是一个模块。 注意 有的资源只包含 一堆代码 ,而不是通过define函数正规的定义一个模块。
    5. Evaluated:  模块通过define函数定义完成。 loader  加载器已经运行完工厂函数, 并返回结果。
  • cacheBust: "",  添加到模块URL后的查询字符串,破坏浏览器的缓存
  • cache: {}, 散列对像: (mid | url) --> (function | string), 资源的缓存列表。 可使用 mid-->function 或者 url-->string 从config.cache中获得一个资源。 url键跟mid键的区别就是包含 "url:"前缀.  url键提供的代表资源的url字符串。 mid键提供的是函数值。  (没有实际的例子举例,大家可以先忽略不管)
  • urlKeyPrefix = "url:",  在cache中添加到url键前面的字符串
  • pendingCacheInsert: hash:(mid)-->(function), 给定插入到cache中的缓存模块集合。 当缓存模块已经被发布到loader,  它们就会进入到 pendingCacheInsert;  然后根据AMD  define(1)或者 接受其它独立缓存模块,压入 cache (没明白), 第(1) 是常见的情况。  (没有实际的例子举例,大家可以先忽略不管)
  • dojoSniffConfig: {}, 一个关于配置变量的映射, 保存从 data-dojo-config中侦测到的配置数据
  • inertPointSibling =0 ,  节点用于定位角本(请求资源的角本)被插入到文档的哪个位置

配置核心

配置主要用来初始化一些之后会用到的包的信息,别名,map, paths, 以及缓存等。 实际开发中,开发者会在编译Dojo指定这些配置。 

配置有两种方法。一种是在指定 dojo-config-api时, loader会根据开发者指定的map, paths来自动计算 mapProgs, pathsMapProg. 

如果 dojo-config-api为false, 则需要手动defaultConfig指定以下选项, 这种方法很少使用。这里只是告诉你,dojo的配置机制,会对哪些方面进行配置。
// 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;

如果dojo-config-api 为true. 会分以下三步进行配置,配置的核心函数是conifg= function(config, booting, referenceModule){}.  首先对defaultConfig 进行配置,之后是 userConfig , 最后是对侦测到的 sniffConfig.

配置的代码流程如下配置的代码流程如下:

if(has("dojo-config-api")){
var consumePendingCacheInsert = function(referenceModule){},
escapeString = function(s){
            
            },




			computeMapProg = function(map, dest){
				
			},




			computeAliases = function(config, dest){
				
			},








			fixupPackageInfo = function(packageInfo){
				
			},




			delayedModuleConfig
				
				= [],




			config = function(config, booting, referenceModule){




				
			};




	
       
		if(has("dojo-cdn") || has("dojo-sniff")){
			/*
               1. 如果在script中有指定dojo.js, 则获取 dojo.js的文件夹路径,做为baseUrl. 并获得data-dojo-config 或者 djConfig的数据。
               2. 记录script节点, 并赋值给变量insertPointSibling. 用于定位之后的角本注入位置。
               
            */
		}
        
		if(has("dojo-test-sniff")){
			/*
                在DOH单元测试时,将测试的配置添加到dojoSniffConfig对像中
            */
		}




		
		req.rawConfig = {};  //原生的配置
		config(defaultConfig, 1);  //进行默认配置




		
		if(has("dojo-cdn")){
			//如果dojo.js在CDN上,指定dojo, dijit, dojox等包的文件路径.
		}




		config(userConfig, 1); //进行用户配置








		config(dojoSniffConfig, 1);  //对侦测到的配置选项,进行处理




}



  1. userConfig 和 defaultConfig 都是通过初始化参数传递给Dojo. 而在

你可能感兴趣的:(dojo)