require.js源碼分析

寫的寥寥草草,博客園的佈局怎麼弄尚未研究,再保存一份草稿,往後在完善,深度研究javascript

require.js 加載順序html

1:加載html主頁,require.js文件
2:腳本執行到html中的script開始使用require.js進行模塊化加載。先將文件路徑加載保存在map中。在調用require()時並無進行相應文件的加載
3:require配置,在初始化config中到底作了些什麼事情呢(真正對用的初始化函數是function(deps, callback, errback, optional)這裏包括了須要定義的config基本屬性,回掉函數,錯誤處理函數,optional)
3.1肯定context使用require.js中默認的定義
3.2config基本屬性不是數組與字符串(在這裏的數組判斷挺有意思,沒有使用typeof而是使用了Object.prototype.toString.call native方法):{
保存的config 基本屬性
若是第二個參數數組,則callback默認也是config基本屬性的一項
}
3.3config基本屬性中是否包含context項
3.4驗證是否已經擁有該context,一樣使用了Objet.prototype.hasOwnProperty.call native方法。contextName畢竟只是一個臨時變量,真正最後要用的仍是require.js中的剛開始聲明的那些全局變量
3.5最後將config基本屬性添加到context上下文本對象中的config屬性中,默認的一個config包含了如上幾個基本屬性
                eachProp(cfg, function (value, prop) {
                    if (objs[prop]) {
                        if (!config[prop]) {
                            config[prop] = {};
                        }
                        mixin(config[prop], value, true, true);
                    } else {
                        config[prop] = value;
                    }
                });
 
    function eachProp(obj, func) {
        var prop;
        for (prop in obj) {
            if (hasProp(obj, prop)) {
                if (func(obj[prop], prop)) {
                    break;
                }
            }
        }
    }
 
    function mixin(target, source, force, deepStringMixin) {
        if (source) {
            eachProp(source, function (value, prop) {
                if (force || !hasProp(target, prop)) {
                    if (deepStringMixin && typeof value === 'object' && value &&
                        !isArray(value) && !isFunction(value) &&
                        !(value instanceof RegExp)) {
 
                        if (!target[prop]) {
                            target[prop] = {};
                        }
                        mixin(target[prop], value, force, deepStringMixin);
                    } else {
                        target[prop] = value;
                    }
                }
            });
        }
        return target;
    }
 
    function each(ary, func) {
        if (ary) {
            var i;
            for (i = 0; i < ary.length; i += 1) {
                if (ary[i] && func(ary[i], i, ary)) {
                    break;
                }
            }
        }
    }
可是在eachProp中咱們也看到了,能夠添加自定義屬性,mixin config中  , packages必須是數組,其餘都是object對象,
3.6建立上下文對象context的equire屬性。至此全部的script中的順序動做已經完成了
            makeRequire: function (relMap, options) {
                options = options || {};
 
                function localRequire(deps, callback, errback) {
                    var id, map, requireMod;
 
                    if (options.enableBuildCallback && callback && isFunction(callback)) {
                        callback.__requireJsBuild = true;
                    }
 
                    if (typeof deps === 'string') {
                        if (isFunction(callback)) {
                            //Invalid call
                            return onError(makeError('requireargs', 'Invalid require call'), errback);
                        }
 
                        //If require|exports|module are requested, get the
                        //value for them from the special handlers. Caveat:
                        //this only works while module is being defined.
                        if (relMap && hasProp(handlers, deps)) {
                            return handlers[deps](registry[relMap.id]);
                        }
 
                        //Synchronous access to one module. If require.get is
                        //available (as in the Node adapter), prefer that.
                        if (req.get) {
                            return req.get(context, deps, relMap, localRequire);
                        }
 
                        //Normalize module name, if it contains . or ..
                        map = makeModuleMap(deps, relMap, false, true);
                        id = map.id;
 
                        if (!hasProp(defined, id)) {
                            return onError(makeError('notloaded', 'Module name "' +
                                        id +
                                        '" has not been loaded yet for context: ' +
                                        contextName +
                                        (relMap ? '' : '. Use require([])')));
                        }
                        return defined[id];
                    }
 
                    //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();
                    });
 
                    return localRequire;
                }
 
                mixin(localRequire, {
                    isBrowser: isBrowser,
 
                    /**
                     * Converts a module name + .extension into an URL path.
                     * *Requires* the use of a module name. It does not support using
                     * plain URLs like nameToUrl.
                     */
                    toUrl: function (moduleNamePlusExt) {
                        var ext,
                            index = moduleNamePlusExt.lastIndexOf('.'),
                            segment = moduleNamePlusExt.split('/')[0],
                            isRelative = segment === '.' || segment === '..';
 
                        //Have a file extension alias, and it is not the
                        //dots from a relative path.
                        if (index !== -1 && (!isRelative || index > 1)) {
                            ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
                            moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
                        }
 
                        return context.nameToUrl(normalize(moduleNamePlusExt,
                                                relMap && relMap.id, true), ext,  true);
                    },
 
                    defined: function (id) {
                        return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
                    },
 
                    specified: function (id) {
                        id = makeModuleMap(id, relMap, false, true).id;
                        return hasProp(defined, id) || hasProp(registry, id);
                    }
                });
 
                //Only allow undef on top level require calls
                if (!relMap) {
                    localRequire.undef = function (id) {
                        //Bind any waiting define() calls to this context,
                        //fix for #408
                        takeGlobalQueue();
 
                        var map = makeModuleMap(id, relMap, true),
                            mod = getOwn(registry, id);
 
                        removeScript(id);
 
                        delete defined[id];
                        delete urlFetched[map.url];
                        delete undefEvents[id];
 
                        //Clean queued defines too. Go backwards
                        //in array so that the splices do not
                        //mess up the iteration.
                        eachReverse(defQueue, function(args, i) {
                            if(args[0] === id) {
                                defQueue.splice(i, 1);
                            }
                        });
 
                        if (mod) {
                            //Hold on to listeners in case the
                            //module will be attempted to be reloaded
                            //using a different config.
                            if (mod.events.defined) {
                                undefEvents[id] = mod.events;
                            }
 
                            cleanRegistry(id);
                        }
                    };
                }
 
                return localRequire;
            },
 
 
        function intakeDefines() {
            var args;
 
            //Any defined modules in the global queue, intake them now.
            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);
                }
            }
        }
 
可是在require屬性中有啓用一個setTimeout定時間隔任務
 
    req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
        setTimeout(fn, 4);
    } : function (fn) { fn(); };
 
        function intakeDefines() {
            var args;
 
            //Any defined modules in the global queue, intake them now.
            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);
                }
            }
        }
 
        function takeGlobalQueue() {
            //Push all the globalDefQueue items into the context's defQueue
            if (globalDefQueue.length) {
                //Array splice in the values since the context code has a
                //local var ref to defQueue, so cannot just reassign the one
                //on context.
                apsp.apply(defQueue,
                           [defQueue.length, 0].concat(globalDefQueue));
                globalDefQueue = [];
            }
        }
 
獲取module
          function getModule(depMap) {
            var id = depMap.id,
                mod = getOwn(registry, id);
 
            if (!mod) {
                mod = registry[id] = new context.Module(depMap);
            }
 
            return mod;
        }
通過幾回循環調用後,終於load到了第一個moudle,是在makeModuleMap方法中url等信息的拼裝,保存到了depMaps中。同時depCount的計數值也加一了。這也就是require.js最重要的模塊化,將全部的要加載文件經過一個map對下保存,每一個文件用require.js的moudle進行封裝,封裝了什麼東西呢?
這個封裝而不是單獨的封裝一個module對象,而是先進性了require的對象-》context對象-》module對象
 
 
 
對於module的加載過程當中函數的執行順序?
nextTick循環調用
-intakeDefines
--takeGlobalQueue
-getModule
--makeModuleMap
---splitPrefix
---normalize
----trimDots
---splitPrefix
---nameToUrl
---Module
-Module.init
-- Module.enable
-- - Module.enable
---Module.check
---Module. emit
-checkLoaded
注意:若是經過require.js模塊加載的第一個文件加載進來後,由於require.js尚未結束,DOM加載還須要繼續,因此還會順序運行新進來的文件。
 
 
check
fetch
enable  
callPlugin
load 
 
跳回到context
load
跳回到require
load
最後真正實用的仍是req.load加載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);
 
            //Set up load listener. Test attachEvent first because IE9 has
            //a subtle issue in its addEventListener and script onload firings
            //that do not match the behavior of all other browsers with
            //addEventListener support, which fire the onload event for a
            //script right after the script execution. See:
            //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
            //UNFORTUNATELY Opera implements attachEvent but does not follow the script
            //script execution mode.
            if (node.attachEvent &&
                    //Check if node.attachEvent is artificially added by custom script or
                    //natively supported by browser
                    //read https://github.com/jrburke/requirejs/issues/187
                    //if we can NOT find [native code] then it must NOT natively supported.
                    //in IE8, node.attachEvent does not have toString()
                    //Note the test for "[native code" with no closing brace, see:
                    //https://github.com/jrburke/requirejs/issues/273
                    !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
                    !isOpera) {
                //Probably IE. IE (at least 6-8) do not fire
                //script onload right after executing the script, so
                //we cannot tie the anonymous define call to a name.
                //However, IE reports the script as being in 'interactive'
                //readyState at the time of the define call.
                useInteractive = true;
 
                node.attachEvent('onreadystatechange', context.onScriptLoad);
                //It would be great to add an error handler here to catch
                //404s in IE9+. However, onreadystatechange will fire before
                //the error handler, so that does not help. If addEventListener
                //is used, then IE will fire error before load, but we cannot
                //use that pathway given the connect.microsoft.com issue
                //mentioned above about not doing the 'script execute,
                //then fire the script load event listener before execute
                //next script' that other browsers do.
                //Best hope: IE10 fixes the issues,
                //and then destroys all installs of IE 6-9.
                //node.attachEvent('onerror', context.onScriptError);
            } else {
                node.addEventListener('load', context.onScriptLoad, false);
                node.addEventListener('error', context.onScriptError, false);
            }
            node.src = url;
 
            //For some cache cases in IE 6-8, the script executes before the end
            //of the appendChild execution, so to tie an anonymous define
            //call to the module name (which is stored on the node), hold on
            //to a reference to this node, but clear after the DOM insertion.
            currentlyAddingScript = node;
            if (baseElement) {
                head.insertBefore(node, baseElement);
            } else {
                head.appendChild(node);
            }
            currentlyAddingScript = null;
 
            return node;
        } else if (isWebWorker) {
            try {
                //In a web worker, use importScripts. This is not a very
                //efficient use of importScripts, importScripts will block until
                //its script is downloaded and evaluated. However, if web workers
                //are in play, the expectation that a build has been done so that
                //only one script needs to be loaded anyway. This may need to be
                //reevaluated if other use cases become common.
                importScripts(url);
 
                //Account for anonymous modules
                context.completeLoad(moduleName);
            } catch (e) {
                context.onError(makeError('importscripts',
                                'importScripts failed for ' +
                                    moduleName + ' at ' + url,
                                e,
                                [moduleName]));
            }
        }
    };
 
建立一個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;
    };
繼續組裝script node 
head = s.head = document.getElementsByTagName('head')[0];
在這裏require.Js將script node添加到了原始html中的head標籤內
新生成的head,同時瀏覽器也對js文件進行了加載
 
最後有意思的是require.js在 checkLoaded時 居然作了一遍remove,這個仍是蠻有意思的,
 



相關文章
相關標籤/搜索