博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一步一步带你分析 requirejs
阅读量:6322 次
发布时间:2019-06-22

本文共 9849 字,大约阅读时间需要 32 分钟。

详细源代码一共就2000多行,来看我这篇分析的同学应该都下载下来了,好了,话不多说,开始:

 

代码的开头就出现3个全局变量: requirejs, require, define

var requirejs, require, define;(function(global, setTimeout){ balababla......})(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)))

  

require 和 define 大家应该都知道上干什么的,说实话,我是不知道的,在分析代码的时候,我从来也没用过这个框架,就听过AMD,就来直接看源码了。

如果你也不是很清楚,这2个变量是干什么的,我就来简单介绍一下,懂得的同学要是发现我说错了,希望指点我也一下。

 主页面 index.html:

  注意src是引入我们的requirejs库,  data-main:就是我们第一次用requrie的地方:

 

      

 main.js:

  这里2个代码块都是依赖require的:

(1)require.config:配置

(2)requrie(); 加载需要的函数,注意里面的 ['name', 'say'],其实都是文件名,它们都在./js/ 目录下,具体看conifg

 

require.config({  baseUrl: '',  paths: {    'nameDep': 'js/nameDep',    'say': 'js/say',    'name': 'js/name'  },  shim: {    'name': {      deps: ['nameDep']    }  }});require(['name', 'say'], function (name, say) {  say(name);});

  

./js/name.js  和 ./js/say.js

//namedefine([''], function () {  return '测试';});

  

//saydefine([], function () {  return function (name) {    console.log(name);  };});

  

最后注意在config中有个skim,这里面也是定义js文件的,只是由于他可能不符合AMD加载的规范

 ./js/nameDep.js

 

console.log("nameDep.js")

  

以上分析,我们大概看得出  require 和 define 都是 function 

--------------------------------------分割线-------------------------------------------------------------------------

下面进入2000多行匿名函数的讲解:

首先说一下2个参数: global 和 setTimeout 

(function (global, setTimeout) { })(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout))

  

如果在浏览器的环境中,就是平时我们所知的  windows对象 和 windows.setTimeout方法 。本文只讨论浏览器环境,别的环境咱不考虑。

 

接下来就是一大堆的变量和函数的定义,太多了!反正我就扫了一眼,记不住?无所谓,只需要记住这开头这3个变量就行了:req,contexts,cfg

 

2个是空对象  cfg,contexts ,还有一个函数 req 。

 

为什么它是函数??? 你一拉到底,一定会看到 req = requirejs = function(){} 的定义,眼神不好也没关系,看我下面的代码框里有req({});

 

var req,        ....       contexts = {},        cfg = {},       ....      //各种function定义      ....        //Create default context.      req({});           .....

  

好了把眼神定位到 req({})吧, 因为这是上面一大堆定义后,第一次执行了代码!!!!

 

req这个函数长什么样子?? 你用文字匹配往上找:

 

/**     * Main entry point.     *     * If the only argument to require is a string, then the module that     * is represented by that string is fetched for the appropriate context.     *     * If the first argument is an array, then it will be treated as an array     * of dependency string names to fetch. An optional function callback can     * be specified to execute when all of those dependencies are available.     *     * Make a local req variable to help Caja compliance (it assumes things     * on a require that are not standardized), and to give a short     * name for minification/local scope use.     */    req = requirejs = function (deps, callback, errback, optional) {        //Find the right context, use default        var context, config,            contextName = defContextName; //开头定义了 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 = [];            }        }           //第一次没有config.context 跳过        if (config && config.context) {            contextName = config.context;        }           //第一次context == undefined        context = getOwn(contexts, contextName);           //第一次进入        if (!context) {            //只有第一次会调用newContext("_")            context = contexts[contextName] = req.s.newContext(contextName);        }                if (config) {            context.configure(config);        }        return context.require(deps, callback, errback);    };

  

上面代码  context出现的频率非常高,说明这小子很重要。那我们来看看这小子到底是啥!!!

 

直接找到它的定义: 

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

  

  那 newContext 又是什么鬼! 搜它!!!!!

  

function newContext(contextName) {        var context          ...       //一大堆function定义         ....        context={              .....                   }        context.require = context.makeRequire();        return context;    }

  

用20秒的时间扫一眼!老套路,一大堆的变量,函数的定义。 一直到最后return 一个 context变量。

再往上拉看看 context 到底是个什么鬼!!

记忆力没毛病的话,发现context对象里装的全是刚才定义过一些变量,函数。

-----------------------------------------------------------------------------------------------

|    其中return的上一行代码:                                                                   |

|              context.require = context.makeRequire();                                  |

|      有兴趣就去看一眼,很简单,就是返回一个叫localRequire的闭包     |

------------------------------------------------------------------------------------------------

OK,这个newContext函数我们已经差不多了解了,就是返回一个对象,通过这个对象控制newContext里的一系列私有变量和对象。

我们的contexts就如下图所示:

 

 

接下去走 

      context.configure(config);

我建议你去看一下,因为里面啥也没干,你只需要花10秒左右的时间就可以看完。。。

 

然后走

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

看上面的图,context.require 就是 localRequire

 

接下来,我们走进localRequire的世界里一探究竟!!!!其中deps = [ ]

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

  

很快我们就把目光锁定到(因为,前面的一大段代码都是if语句,不符合条件进不去)

//Grab defines waiting in the global queue.                    intakeDefines();

  

intakeDefines --》takeGlobalQueue--》context.nextTick--》intakeDefines --》  requireMod.init --》  checkLoaded();

 

里面大致就是上面所示,因为有点复杂,我这里暂时先不说,接续走简单的。。

 

到此为止,我们已经走完了一边req。

大致流程如下图所示:

 

 

接下去,我们再一拉到底!!!!

倒数第二行:

 

//Set up with config info.    req(cfg);

 

cfg还记得是什么吗?? 一开始是定义为空对象啊!!!

 

有地方修改过它吗? 往上拉一拉!!

//Look for a data-main script attribute, which could also adjust the baseUrl.  if (isBrowser && !cfg.skipDataMain) {    //Figure out baseUrl. Get it from the script tag with require.js in it.    eachReverse(scripts(), function(script) {      //Set the 'head' where we can append children by      //using the script's parent.      if (!head) {        head = script.parentNode;      }      //Look for a data-main attribute to set main script for the page      //to load. If it is there, the path to data main becomes the      //baseUrl, if it is not already set.      dataMain = script.getAttribute('data-main');      if (dataMain) {        //Preserve dataMain in case it is a path (i.e. contains '?')        mainScript = dataMain;         //Set final baseUrl if there is not already an explicit one,        //but only do so if the data-main value is not a loader plugin        //module ID.        if (!cfg.baseUrl && mainScript.indexOf('!') === -1) {          //Pull off the directory of data-main for use as the          //baseUrl.          src = mainScript.split('/');          mainScript = src.pop();          subPath = src.length ? src.join('/') + '/' : './';          cfg.baseUrl = subPath;        }        //Strip off any trailing .js since mainScript is now        //like a module name.        mainScript = mainScript.replace(jsSuffixRegExp, '');        //If mainScript is still a path, fall back to dataMain        if (req.jsExtRegExp.test(mainScript)) {          mainScript = dataMain;        }        //Put the data-main script in the files to load.        cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];        return true;      }    });  }

  

这里面做的事情很简单,就是找到页面中包含 "data-main" 属性的script标签,分析路径。

最后得到这样的结果:

 

 

好了。再回到req(cfg);

这一次我们好好的走一走上面流程图里的步骤。。。。

 

把目光迅速锁定到:

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

  

 

 

转载于:https://www.cnblogs.com/huenchao/p/7141812.html

你可能感兴趣的文章
给awstats增加纯真IP库qqwry.dat支持
查看>>
PhalApi-RabbitMQ基于PhalApi专业队列拓展
查看>>
ubuntu12.04--无法获得锁 /var/lib/dpkg/lock - open (1...
查看>>
git冲突 head
查看>>
OSChina 周二乱弹 —— 啥时候能低调的炫个富?
查看>>
OSChina 周五乱弹 —— 吃货的自我修养
查看>>
OSChina 周六乱弹 —— 为啥你没对象
查看>>
OSChina 周六乱弹 —— 生命诚可贵,啤酒价更高
查看>>
今天晚上同事出现这个问题 Eclipse 中 Tomcat启动卡100%(preparing launch delegate...)...
查看>>
jquery中的 $(#id)与document.getElementById( id )的区别
查看>>
Android动画 详解(二 帧动画 属性动画 )
查看>>
MyISAM静态
查看>>
C#二次封装虹软arcface
查看>>
ngrok 安装入门
查看>>
cocoaPod使用教程
查看>>
powerdesigner概念模型
查看>>
TCP通信中的粘包问题
查看>>
MySQL limit查询优化(数据量大的时候很优)
查看>>
Apache access.log error.log日志文件太大优化方法
查看>>
文件自动备份工具
查看>>