zoukankan      html  css  js  c++  java
  • 一步一步带你分析 requirejs

    详细源代码一共就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的地方:

    <html>
    <head>
        <title></title>
    </head>
    <body>
      <script src="require.js" type="text/javascript" data-main="main.js"></script>
    </body>
    </html>
    

     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

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

      

    //say
    define([], 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);
    

      

     

  • 相关阅读:
    Catalan数
    C# & LINQ 对象克隆
    Rotate Image
    反转链表
    QtCreator调试程序时GDB崩溃
    Regular Expression Matching
    Wildcard Matching
    DFA与NFA
    Set Matrix Zeroes
    PCA原理
  • 原文地址:https://www.cnblogs.com/huenchao/p/7141812.html
Copyright © 2011-2022 走看看