ReqiureJS实现原理(一)

来源:互联网 发布:淘宝客服提成2个点 编辑:程序博客网 时间:2024/06/07 01:56

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');})

执行结果

ba.js:2 amain.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: Objectconfigure: function (cfg) {contextName: "_"defQueue: Array[0]defined: Objectenable: 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: Objectrequire: 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行。
只是这列有一个疑问,什么是全局序列,为什么会存在全局序列?

0 0
原创粉丝点击