关于requirejs的使用大家一定不陌生,可是requirejs是如何通过依赖注入的方式进行模块加载的呢?为什么说requirejs里的模块都是提前加载而不是所谓的按需加载呢。下面就让我们深(装)入(个)地(叉)了解一下requirejs的源码吧!
requirejs执行的第一个函数
//Create default context.
req({});
传入的参数是一个空对象,创建一个默认的上下文
newContext方法是requirejs源码中最长的一部分,由于context是一个全局的变量,在实例化newContext之后生成了一个闭包,维持了newContext内部定义函数和变量的引用。它主要包括了以下几个部分:
1、常量部分
2、主要工具方法
3、 handler相关
4、Module相关
5、context相关
以下便是执行了一次空对象参数的context
在有了context这个主要对象后,调用context.configure方法配置config参数
configure方法源码如下
不过由于config也是个空对象,所以这里似乎啥都没做,继续往下看
这里的require方法也是一个很重要的方法。在newContext方法的最后一行定义了context.require方法
context.require方法又是context里面定义的makeRequire方法
最后可以看到其实就是localRequire方法。那么localRequire方法又干了啥呢?我们进入到localRequire方法里面看一看
由于三个参数一个是空对象,另外两个undefined,所以直接跳到了下面这里
通过注释我们大概可以明白这个方法是对全局队列里的对象进行处理
takeGlobalQueue方法将globalDefQueue里面的定义的模块移到context的defQueue(defQueue中可能本来就是有值的)中:
然后再一个一个地进行执行callGetModule方法执行模块调用
关于callGetModule方法留着下次再讲,这一条线看下去脑容量已经不够了
讲到这里其实已经有些脱节了,我们还是先看一下全局队列globalDefQueue是怎么产生的。在使用requirejs进行模块定义的时候一般都会使用define函数
define函数的定义在源码的最下方
define函数的最后一行
从代码逻辑可知,在没有context上下文环境的时候,会往GlobalDefQueue中push新define的模块,如果有context上下文,则会往context.defQueue中push新define的模块。
最后再回到上面的localRequire方法
这里定义了一个异步的方法去做模块初始化的事情
这里用将回调方法通过setTimeOut加入到事件队列中,我觉得是为了让模块的加载顺序按照预期顺序执行,不然可能因为下载速度等原因导致顺序错乱。
至于再这个4ms的延时回调里面模块初始化方法究竟做了什么,我们下次再看吧。
总结:
1、传入一个空对象初次执行require函数,主要是创建了一个context对象,里面封装了各种工具方法和module类。
2、通过nextTick添加异步事件进行模块的初始化
3、通过define定义的模块都会添加到GlobalDefQueue或是context.defQueue中,在执行localRequire方法的时候会遍历这两个队列提取新define的模块进行后续处理
4、明天继续研究localRequire方法、callGetModule方法和nextTick中的回调方法
其实requirejs的源码对于我这个前端菜鸟而言还是相当有难度的,特别是各种回调看得晕乎乎的,每次看完之后都忘的差不多了。所以这一次想通过博客的方式重新温习一遍,并且记录下来。