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行。
只是这列有一个疑问,什么是全局序列,为什么会存在全局序列?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值