ReqiureJS實現原理(一)

requireJS三個測試文件

main.js

require.config({
    path: {
        "a": "a",
        "b": "b"
    }
})
require(['a'], function(a) {
    console.log('main');
})

a.js

define(['b'], function(b) {
    console.log('a');
})

執行結果

b
a.js:2 a
main.js:8 main

可以看到執行順序 b,a.js main.js,即加載完b.js->a.js->main.js

源碼閱讀

構造上下文

打開調試窗口:
這段代碼初始化了一個上下文對象context,調用的是req方法
這裏寫圖片描述

 req = requirejs = function(deps, callback, errback, optional) {

        //找到上下文信息,用default:defContextName
        var context, config,
            contextName = defContextName;

        // Determine if have config object in the call.
        if (!isArray(deps) && typeof deps !== 'string') {
            // deps is a config object
            config = deps;
            if (isArray(callback)) {
                // Adjust args if there are dependencies
                deps = callback;
                callback = errback;
                errback = optional;
            } else {
                deps = [];
            }
        }

1.判斷是否是數組

dep傳入的參數是一個數組[‘a’],因爲我們是require([‘a’])
require還可以加載很多模塊,都是通過數組的參數傳入。例如

 require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
  });

定義瞭如果deps爲非數組的處理方式

 //requireJS判斷數組的方式
 var op = Object.prototype,
    ostring = op.toString;

function isArray(it) {
    return ostring.call(it) === '[object Array]';
}

當然,我們這裏是array,這段函數就跳過去了,如果非數組就拋出異常了;

下一步

2.判斷是否是數組

判斷是否有config,沒有配置參數,也跳過了

if (config && config.context) {
            contextName = config.context;
        }

這裏寫圖片描述

config && config.context=undefined,因爲我們沒有指定參數

3.構造上下文

3.1檢測上下文
context=getOwn(contexts,contextName)

這裏寫圖片描述

81-87

    var hasOwn = op.hasOwnProperty, //21

    //判斷obj是否有prop屬性
    function hasProp(obj, prop) {
        return hasOwn.call(obj, prop);
    }

    function getOwn(obj, prop) {
        return hasProp(obj, prop) && obj[prop];
    }

這裏寫圖片描述
這裏的obj是Object,prop是”_“
這裏寫圖片描述
hasProp(obj, prop)返回的是true
這裏寫圖片描述

首先檢測obj是否含有prop的屬性,如果有的話,就返回obj[prop],沒有就返回false ,返回的是一個obj._ 也就是Context對象
這裏寫圖片描述

Module: function (map) {
completeLoad: function (moduleName) {
config: Object
configure: function (cfg) {
contextName: "_"
defQueue: Array[0]
defined: Object
enable: function (depMap) {
execCb: function (name, callback, args, exports) {
load: function (id, url) {
makeModuleMap: function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
makeRequire: function (relMap, options) {
makeShimExports: function (value) {
nameToUrl: function (moduleName, ext, skipExt) {
nextTick: function (fn) {
onError: function onError(err, errback) {
onScriptError: function (evt) {
onScriptLoad: function (evt) {
registry: Object
require: function localRequire(deps, callback, errback) {
urlFetched: Object
__proto__: Object

這裏context對象已經存在了,其實我都不知道什麼時候存在的,難道是前幾次加載的時候寫進去的?(相當於緩存)

        if (!context) {
            context = contexts[contextName] = req.s.newContext(contextName);
        }
3.2如果沒有上下文,則構造

前面看到context的結構,那麼context是如何構造的呢?
這裏有一段代碼,因爲每次構造新的context的時候,都會保存進contexts,也就是說已經構建過的依賴將存在contexts中;

//Used to filter out dependencies that are already paths.
    req.jsExtRegExp = /^\/|:|\?|\.js$/;
    req.isBrowser = isBrowser;
    s = req.s = {
        contexts: contexts,
        newContext: newContext
    };

源碼198-1751都在構造上下文,這裏就先跳過。
我們只要知道context的內容就好了。
構造完畢後,第一步進入了context.makeRequire函數
這裏寫圖片描述
1449-1668行 :localRequire()函數


                    //Grab defines waiting in the global queue.
                    intakeDefines();

                    //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();

//將defines放在全局序列中 reqiure中的方法,不是context中的方法了
intakeDefines();


        function intakeDefines() {
            var args;

            //Any defined modules in the global queue, intake them now.
            //如果全局隊列有defined modules,現在就進入它們
            takeGlobalQueue();

            //Make sure any remaining defQueue items get properly processed.
            while (defQueue.length) {
                args = defQueue.shift();
                if (args[0] === null) {
                    return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' +
                        args[args.length - 1]));
                } else {
                    //args are id, deps, factory. Should be normalized by the
                    //define() function.
                    callGetModule(args);
                }
            }
            context.defQueueMap = {};
        }

takeGlobalQueue()是一個內部方法,將全局序列轉化爲context化的序列,具體如何實現的就不細說了,代碼在558-570行。
只是這列有一個疑問,什麼是全局序列,爲什麼會存在全局序列?

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