requirejs源码解析之运行流程

说明:
内容主要包括三部分:
1.按源码的结构顺序 对 所有的变量及方法的说明
2.requirejs运行流程
3、流程相关图片

一、源码的结构

为了方便比对源码,按源码的结构顺序展示。

var requirejs, require, define;
(function (global, setTimeout) {
    var req, s, head, baseElement, dataMain, src,
        interactiveScript, currentlyAddingScript, mainScript, subPath,
        version = '2.3.5',
        commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg,            //去除注释
        cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,//提取require函数的arguments
        jsSuffixRegExp = /\.js$/,
        currDirRegExp = /^\.\//,
        op = Object.prototype,
        ostring = op.toString,
        hasOwn = op.hasOwnProperty,
        isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
        isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
        readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
                      /^complete$/ : /^(complete|loaded)$/,
        defContextName = '_',
        isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
        contexts = {},
        cfg = {},
        globalDefQueue = [],
        useInteractive = false;
    //返回singlePrefix或空
    function commentReplace(match, singlePrefix) {}
    //判断函数
    function isFunction(it) {}
    //判断数组
    function isArray(it) {}
    //执行函数func(ary[i], i, ary);返回真值,跳出循环
    function each(ary, func) {}
    //与each序列反
    function eachReverse(ary, func) {}
    //判断obj是否有prop
    function hasProp(obj, prop) {}
    //返回obj上的prop
    function getOwn(obj, prop) {}
    //循环调用func(obj[prop], prop);返回真值,跳出循环
    function eachProp(obj, func) {}
    //混合source属性值(target没有同名的)到target
    //force为真,target同名覆盖,deepStringMixin为真,深混合
    function mixin(target, source, force, deepStringMixin) {}
    //返回逆名函数,执行为obj调用fn函数
    function bind(obj, fn) {}
    //返回script元素的集合
    function scripts() {}
    //throw err;
    function defaultOnError(err) {}
    //例getGlobal("aa.bb");为global.aa.bb
    function getGlobal(value) {}
    //生成一个错误
    function makeError(id, msg, err, requireModules) {}
    if (typeof define !== 'undefined') {
        return;
    }
    if (typeof requirejs !== 'undefined') {
        if (isFunction(requirejs)) {
            return;
        }
        cfg = requirejs;
        requirejs = undefined;
    }
    if (typeof require !== 'undefined' && !isFunction(require)) {
        cfg = require;
        require = undefined;
    }
    function newContext(contextName) {
        var inCheckLoaded, Module, context, handlers,
            checkLoadedTimeoutId,
            config = {
                waitSeconds: 7,
                baseUrl: './',
                paths: {},
                bundles: {},
                pkgs: {},
                shim: {},
                config: {}
            },
            registry = {},
            enabledRegistry = {},
            undefEvents = {},
            defQueue = [],
            defined = {},
            urlFetched = {},
            bundlesMap = {},
            requireCounter = 1,
            unnormalizedCounter = 1;
        //ary中.删除此项;..删此项和前一项除(i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1]==='..')
        function trimDots(ary) {}
        //路径处理.config.pkgs有name值优先,无值按相对路径转化,apply是否启用地图配置
        function normalize(name, baseName, applyMap) {}
        //删除data-requiremoduley为name和data-requirecontext === context.contextName的script
        function removeScript(name) {}
        //先移除再加载模块;
        function hasPathFallback(id) {}
        //第一个"!"分离的前后数据 return [prefix, name];
        function splitPrefix(name) {}
        //返回模块的属性对象
        // return {
        //         prefix: prefix,
        //         name: normalizedName,
        //         parentMap: parentModuleMap,
        //         unnormalized: !!suffix,
        //         url: url,
        //         originalName: originalName,
        //         isDefine: isDefine,
        //         id: (prefix ?
        //                 prefix + '!' + normalizedName :
        //                 normalizedName) + suffix
        //     };
        function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {}
        //registry[id]有值取值,没值生成新的context.Module对象,并赋给registry[id]
        function getModule(depMap) {}
        //模块加载完且name为defined 或 加载出错且name为error ? 执行fn : 模块绑定事件 
        function on(depMap, name, fn) {}
        //errback ? 执行errback(err) : mod.emit('error', err)执行删除操作
        function onError(err, errback) {}
        //将globalDefQueue推入defQueue
        function takeGlobalQueue() {}
        //commonjs风格
        handlers = {
            //mod.require ? 返回mod.require : localRequire
            'require': function (mod) {},
            'exports': function (mod) {},
            'module': function (mod) {}
        };
        //清除registry[id]、enabledRegistry[id]
        function cleanRegistry(id) {}
        //递归mod.depMaps,执行mod.check();
        function breakCycle(mod, traced, processed) {}
        //检查加载状态,不同状态执行不同操作
        function checkLoaded() {}
        Module = function (map) {
            this.events = getOwn(undefEvents, map.id) || {};
            this.map = map;
            this.shim = getOwn(config.shim, map.id);
            this.depExports = [];
            this.depMaps = [];
            this.depMatched = [];
            this.pluginMaps = {};
            this.depCount = 0;
        };
        Module.prototype = {
            //初始化,根据options.enabled ? this.enable() : this.check()
            init: function (depMaps, factory, errback, options) {},
            //通过this.depCount判断依赖是否加载完成
            defineDep: function (i, depExports) {},
            // map.prefix ? this.callPlugin() : this.load();
            fetch: function () {},
            //通过context.load调req.load加载js文件
            load: function () {},
            //define模块调用
            check: function () {},
            //加载依赖
            callPlugin: function () {},
            //data-main上的模块调用,define模块调用
            enable: function () {},
            //将cb推入this.events[name]
            on: function (name, cb) {},
            //name === 'error'删this.events[name];否则循环this.events[name]执行cb(evt);
            emit: function (name, evt) {}
        };
        //module.init内执行check()非enable();
        function callGetModule(args) {}
        //移除监听事件
        function removeListener(node, func, name, ieName) {}
        //移除监听事件,返回节点
        function getScriptData(evt) {}
        // 获取并加载defQueue中的模块 
        function intakeDefines() {}
        context = {
            config: config,
            contextName: contextName,
            registry: registry,
            defined: defined,
            urlFetched: urlFetched,
            defQueue: defQueue,
            defQueueMap: {},
            Module: Module,
            makeModuleMap: makeModuleMap,
            nextTick: req.nextTick,
            onError: onError,
            //配置参数 调用context.require(cfg.deps || [], cfg.callback);
            configure: function (cfg) {},
            //返回闭包接口供调用
            makeShimExports: function (value) {},
            //返回闭包接口供调用
            makeRequire: function (relMap, options) {
                //makeRequire的实际执行函数,生成宏任务;
                function localRequire(deps, callback, errback) {
                    return localRequire;
                }
                mixin(localRequire, {
                    isBrowser: isBrowser,
                    toUrl: function (moduleNamePlusExt) {},
                    defined: function (id) {},
                    specified: function (id) {}
                });
                if (!relMap) {
                    localRequire.undef = function (id) {};
                }
                return localRequire;
            },
            //调用 module的enable()
            enable: function (depMap) {},
            //完成加载后
            completeLoad: function (moduleName) {},
            //根据moduleName获取url
            nameToUrl: function (moduleName, ext, skipExt) {},
            //调用req.load()
            load: function (id, url) {},
            //return callback.apply(exports, args);
            execCb: function (name, callback, args, exports) {},
            //加载完成后
            onScriptLoad: function (evt) {},
            //加载错误
            onScriptError: function (evt) {}
        };
        context.require = context.makeRequire();
        return context;
    }
    //入口函数
    req = requirejs = function (deps, callback, errback, optional) {};
    //return req(config);
    req.config = function (config) {};
    //宏任务
    req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
        setTimeout(fn, 4);
    } : function (fn) { fn(); };
    //req赋值给require
    if (!require) {
        require = req;
    }
    req.version = version;
    req.jsExtRegExp = /^\/|:|\?|\.js$/;
    req.isBrowser = isBrowser;
    s = req.s = {
        contexts: contexts,
        newContext: newContext
    };
    //初始调用
    req({});
    each([
        'toUrl',
        'undef',
        'defined',
        'specified'
    ], function (prop) {
        req[prop] = function () {
            var ctx = contexts[defContextName];
            return ctx.require[prop].apply(ctx, arguments);
        };
    });
    if (isBrowser) {
        head = s.head = document.getElementsByTagName('head')[0];
        baseElement = document.getElementsByTagName('base')[0];
        if (baseElement) {
            head = s.head = baseElement.parentNode;
        }
    }
    req.onError = defaultOnError;
    //创建节点
    req.createNode = function (config, moduleName, url) {};
    //节点绑定事件,添加到头部,并返回节点
    req.load = function (context, moduleName, url) {};
    //返回状态为interactive的节点
    function getInteractiveScript() {}
    //data-main上的值解析赋给cfg
    if (isBrowser && !cfg.skipDataMain) {
        eachReverse(scripts(), function (script) {
            if (!head) {
                head = script.parentNode;
            }
            dataMain = script.getAttribute('data-main');
            if (dataMain) {
                mainScript = dataMain;
                if (!cfg.baseUrl && mainScript.indexOf('!') === -1) {
                    src = mainScript.split('/');
                    mainScript = src.pop();
                    subPath = src.length ? src.join('/')  + '/' : './';

                    cfg.baseUrl = subPath;
                }
                mainScript = mainScript.replace(jsSuffixRegExp, '');
                if (req.jsExtRegExp.test(mainScript)) {
                    mainScript = dataMain;
                }
                cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
                return true;
            }
        });
    }
    //定义模块的函数
    define = function (name, deps, callback) {};
    define.amd = {
        jQuery: true
    };
    req.exec = function (text) {};
    //将data-main的值解析代入req函数;
    req(cfg);
}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));

二、详细流程

1、初始化变量;
2、执行 req({})

req({})
context = contexts[contextName] = req.s.newContext(contextName);

说明:contextName='_',返回context这个东西,context.require = context.makeRequire();=localRequire;调用makeRequire实际调用makeRequire里的localRequire

context.configure(cfg);

说明:cfg=config={},什么都没干

return context.require(deps, callback, errback);

说明:调用makeRequire里的localRequire;deps=[];

  intakeDefines();
      takeGlobalQueue();

说明:intakeDefines的子函数,两者什么都没执行

 context.nextTick(function () {
                        intakeDefines();
                        requireMod = getModule(makeModuleMap(null, relMap));

                        requireMod.skipMap = options.skipMap;
                        requireMod.init(deps, callback, errback, {
                            enabled: true
                        });
                        checkLoaded();
                    });

说明:产生一个 宏任务1 函数;req({})函数完

3、执行 req(cfg)
获取data-main上的值并解析成cfg

req(cfg);

说明:cfg={baseUrl:"data-main解析值1",deps:[data-main解析值2]}

context = getOwn(contexts, contextName);

说明:获取之前产生的context;

context.configure(cfg);

说明:cfg=config={baseUrl:"data-main解析值1",deps:[data-main解析值2]}

config[prop] = value;

说明:属性值给config

context.require(cfg.deps || [], cfg.callback);

说明:调用makeRequire里的localRequire;deps=[data-main解析值2];

intakeDefines();
takeGlobalQueue();

说明:什么都没执行

context.nextTick(function () {

说明:产生一个 宏任务2 函数;

return context.require(deps, callback, errback);

说明:调用makeRequire里的localRequire;deps=[];

intakeDefines();
takeGlobalQueue();

说明:什么都没执行

context.nextTick(function () {

说明:产生一个 宏任务3 函数;req(cfg);函数完

4、第一个宏任务开始

intakeDefines();
takeGlobalQueue();

说明:什么都没执行

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

说明:返回一个对象obj

nameParts = splitPrefix(name);

说明:name="_@r2"

normalizedName = normalize(name, parentName, applyMap);
url = context.nameToUrl(normalizedName);
parentModule = syms.slice(0, i).join('/');

说明:parentModule="_@r2"

getModule(obj)
new context.Module(depMap)

说明:depMap=上面返回的对象obj;getModule返回context.Module实例requireMod

requireMod.init(deps, callback, errback, {enabled: true});
this.enable();
enabledRegistry[this.map.id] = this;
this.check();
cleanRegistry(id);
this.emit('defined', this.exports);
checkLoaded();

说明:什么都没执行,第一个宏任务完。

5、第二个宏任务开始

intakeDefines();
takeGlobalQueue();

说明:什么都没执行

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

说明:返回一个对象obj

nameParts = splitPrefix(name);

说明:name="_@r3"

normalizedName = normalize(name, parentName, applyMap);
url = context.nameToUrl(normalizedName);
parentModule = syms.slice(0, i).join('/');

说明:parentModule="_@r3"

getModule(obj)
new context.Module(depMap)

说明:depMap=上面返回的对象obj;getModule返回context.Module实例requireMod

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

说明: deps 变为 data-main解析值2

    this.enable();
        enabledRegistry[this.map.id] = this;
        depMap = makeModuleMap(depMap,(this.map.isDefine ? this.map : this.map.parentMap), false,!this.skipMap);
            nameParts = splitPrefix(name);
            normalizedName = normalize(name, parentName, applyMap);                                
            url = context.nameToUrl(normalizedName);                           
"        on(depMap, 'defined', bind(this, function (depExports) {this.defineDep(i, depExports);this.check();}));
                            
                            
                        "
            mod = getModule(depMap);
                mod = registry[id] = new context.Module(depMap);
            mod.on(name, fn);
            getModule(depMap).enable();
        this.check();
checkLoaded();

说明: 宏任务2结束

6、宏任务3同宏任务1

7、执行data-main引入的文件的require函数

require(['./example'],function(example){example.test();});
req = requirejs = function (deps, callback, errback, optional) {
    return context.require(deps, callback, errback);
        intakeDefines();
            takeGlobalQueue();
                context.nextTick(function () {

说明: 产生一个宏任务4函数;require函数结束

onScriptLoad: function (evt) {
    var data = getScriptData(evt);
    context.completeLoad(data.id);
        shim = getOwn(config.shim, moduleName) || {},
        takeGlobalQueue();
        callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);

说明: require函数结束后执行

8、宏任务4同宏任务2
说明: deps变为 要加载的依赖

三、流程相关图片

图片描述

图片描述

图片描述

图片描述

图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值