RequireJS源碼解讀(二)

(續上節)
上面講了如何處理全局序列和context化全局序列
以及執行全局序列中的依賴項。
檢查依賴項

   //Mark all the dependencies as needing to be loaded.
                    context.nextTick(function() {
                        //Some defines could have been added since the
                        //require call, collect them.
                        intakeDefines();

                        requireMod = getModule(makeModuleMap(null, relMap));

                        //Store if map config should be applied to this require
                        //call for dependencies.
                        requireMod.skipMap = options.skipMap;

                        requireMod.init(deps, callback, errback, {
                            enabled: true
                        });

                        checkLoaded();
                    });

最後返回的是 return localRequire;

回到 context.require(deps, callback, errback);

4. 監聽頁面加載

傳入的參數是load加載事件
腳本加載的回調,用於檢查加載狀態。

 onScriptLoad: function(evt) {
                //Using currentTarget instead of target for Firefox 2.0's sake. Not
                //all old browsers will be supported, but this one was easy enough
                //to support and still makes sense.
                if (evt.type === 'load' ||
                    (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
                    //Reset interactive script so a script node is not held onto for
                    //to long.
                    interactiveScript = null;

                    //Pull out the name of the module and the context.
                    var data = getScriptData(evt);
                    context.completeLoad(data.id);
                }

var data = getScriptData(evt) 這個函數 構造完畢之後,將返回一個對象

這裏寫圖片描述
此時data就是{ node:script對象,id : ‘main’ }

5.獲取加載對象

開始加載

 completeLoad: function(moduleName) {
                var found, args, mod,
                    shim = getOwn(config.shim, moduleName) || {},
                    shExports = shim.exports;

                takeGlobalQueue();

                while (defQueue.length) {
                    args = defQueue.shift();
                    if (args[0] === null) {
                        args[0] = moduleName;
                        //If already found an anonymous module and bound it
                        //to this name, then this is some other anon module
                        //waiting for its completeLoad to fire.
                        if (found) {
                            break;
                        }
                        found = true;
                    } else if (args[0] === moduleName) {
                        //Found matching define call for this script!
                        found = true;
                    }

                    callGetModule(args);
                }
                context.defQueueMap = {};

                //Do this after the cycle of callGetModule in case the result
                //of those calls/init calls changes the registry.
                mod = getOwn(registry, moduleName);

這裏寫圖片描述

運行到這裏,callGetModule()就是調用module了吧
這裏寫圖片描述

     function callGetModule(args) {
            //Skip modules already defined.
            if (!hasProp(defined, args[0])) {
                getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
            }
        }

傳入的args :args = [“main”, Array[0], undefined]
args[1]和args[2]到底是來幹嘛的?

5.1 創造ModuleMap對象

首先makeModuleMap(args[0], null, true)).init(args[1], args[2]
從下圖可以看到args[1]是當前加載項的父節點, args[2]->applyMap?:
這裏寫圖片描述

轉化爲絕對路徑

最後將main.js轉化爲url路徑,可能在這裏沒什麼用,但是在這種定義了baseURL或者重命名模塊中,這個normolize顯得非常有用的
這裏寫圖片描述

最後得到的對象:

這裏寫圖片描述

5.2 getModule

這裏寫圖片描述

5.3 Module.init

這裏寫圖片描述

5.4 checkLoaded()

checkLoaded()會調用onScriptLoad(),出現了新的./@r5.js 是a.js嗎
這裏寫圖片描述
然而不是a.js,第二次遍歷加載出了a.js
這裏寫圖片描述

又見到熟悉的@r5.js,這次它變成了Module
這裏寫圖片描述

6 加載第一個script:a.js

 req.load = function(context, moduleName, url) {
        var config = (context && context.config) || {},
            node;
        if (isBrowser) {
            //In the browser so use a script tag
            node = req.createNode(config, moduleName, url);

            node.setAttribute('data-requirecontext', context.contextName);
            node.setAttribute('data-requiremodule', moduleName);

6.1 檢測是否是瀏覽器

 isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),

6.2 創建node

    req.createNode = function(config, moduleName, url) {
        var node = config.xhtml ?
            document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
            document.createElement('script');
        node.type = config.scriptType || 'text/javascript';
        node.charset = 'utf-8';
        node.async = true;
        return node;
    };

傳入的參數:
這個path是在main.js中定義的

require.config({
    path: {
        "a": "a",
        "b": "b"
    }
})

deps=[‘main’],應該main默認就爲依賴項吧,因爲之前在
這裏寫圖片描述

createNode()就是構造一段script,而且指定了charset-set 和async(異步加載);
這裏寫圖片描述

 node.setAttribute('data-requirecontext', context.contextName);
            node.setAttribute('data-requiremodule', moduleName);

這兩句話爲node添加了新的屬性
這裏寫圖片描述

爲node綁定新的事件
 node.addEventListener('load', context.onScriptLoad, false);
 node.addEventListener('error', context.onScriptError, false);

加載事件和error事件都綁定爲context的對象了

其實node就是script,但是require這裏爲它添加了兩個新的屬性和新的load處理事件,我覺得requireJS的精妙之處就在這裏吧。

繼續循環看是否有加載項

這個時候a.js並沒有加載,又出現了.@r5,我猜測b.js要出來了
這裏寫圖片描述
然而我們看到的是,removeScript(“a”),要移除a了?但是a都沒有加載啊
這裏寫圖片描述
不對,這裏是要加載a.js,然而報錯了,應該是因爲我們debug超出了requireJS的時間。
這裏寫圖片描述
.@r6 .@r8出現了

先寫到這裏,requireJS的原理確切是很複雜,經常會對一些全局變量進行修改啥的。
到這裏onScriptLoad()就結束了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章