资讯专栏INFORMATION COLUMN

requirejs源码解析之运行流程

KavenFan / 863人阅读

摘要:说明内容主要包括三部分按源码的结构顺序对所有的变量及方法的说明运行流程流程相关图片一源码的结构为了方便比对源码,按源码的结构顺序展示。

说明:
内容主要包括三部分:
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 = //*[sS]*?*/|([^:""=]|^)//.*$/mg,            //去除注释
        cjsRequireRegExp = /[^.]s*requires*(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变为 要加载的依赖

三、流程相关图片

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107357.html

相关文章

  • 前端模块化和构建工具

    摘要:以前一直对前端构建工具的理解不深,经过几天的研究特意来总结一下,第一次写博客,有写错的请多多见谅,该文章我也从其他博客拷了一些内容,如果有冒犯之处,请指出。强大的设计使得它更像是一个构建平台,而不只是一个打包工具。 以前一直对前端构建工具的理解不深,经过几天的研究特意来总结一下,第一次写博客,有写错的请多多见谅,该文章我也从其他博客拷了一些内容,如果有冒犯之处,请指出。 如今,网页不再...

    ad6623 评论0 收藏0
  • RequireJS:一款优秀的AMD模块加载器

    摘要:概述是一款遵循规范协议的模块加载器,不但能在浏览器端充分利用,同样能在其他的运行时环境,比如和。使用像这样的模块加载器能提高代码的质量和开发速度。一般放在页面的入口出,用来加载其他的模块。 RequireJS概述 RequireJS是一款遵循AMD规范协议的JavaScript模块加载器, 不但能在浏览器端充分利用,同样能在其他的JavaScript运行时环境, 比如Rhino和No...

    syoya 评论0 收藏0
  • seajs 源码解读

    摘要:本文主要简单地解读一下的源码和模块化原理。其中,是这次源码解读的核心,但我也会顺带介绍一下其他文件的作用的。对代码比较简单,其实就是声明一下全局的命名空间。然而,真正的核心在于处理模块依赖的问题。 seajs 简单介绍 seajs是前端应用模块化开发的一种很好的解决方案。对于多人协作开发的、复杂庞大的前端项目尤其有用。简单的介绍不多说,大家可以到seajs的官网seajs.org参看...

    LiangJ 评论0 收藏0
  • requirejs的插件介绍与制作

    摘要:一句化即它是插件的插件,作者事后才发现有这么一个插件绕了不少弯路。这里的主要是为了保存这段内容用于打包使用。免费领取验证码内容安全短信发送直播点播体验包及云服务器等套餐更多网易技术产品运营经验分享请访问网易云社区。文章来源网易云社区 本文由作者郑海波授权网易云社区发布。 前言我这里就不介绍requirejs了, 简而言之: requirejs是支持AMD规范的模块加载器, 事实上它也是...

    shinezejian 评论0 收藏0
  • 从 IIFE 聊到 Babel 带你深入了解前端模块化发展体系

    摘要:我觉得那时他可能并没有料到,这一规则的制定会让整个前端发生翻天覆地的变化。前言 作为一名前端工程师,每天的清晨,你走进公司的大门,回味着前台妹子的笑容,摘下耳机,泡上一杯茶,打开 Terminal 进入对应的项目目录下,然后 npm run start / dev 或者 yarn start / dev 就开始了一天的工作。 当你需要进行时间的转换只需要使用 dayjs 或者 momentj...

    tinylcy 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<