seajs3.0模块加载原理及源码注释

来源:互联网 发布:激战2网络错误 编辑:程序博客网 时间:2024/05/17 09:26

    由于前端业务的复杂,导致前端开发的复杂度日益递增,所以有必要对前端进行模块化划分并进行模块的依赖管理。现在前端模块化主要有COMMONJS、AMD、CMD等标准。目前国内的 seajs 还算比较流行,它是基于CMD标准的,鉴于学习的目的,花了些时间研究了下seajs3.0的源码,这里主要分享一下 模块依赖加载的原理。 同时附上一份简单的源码注解。(作者水平有限,时间也有限,仅供参考,有任何说得不对的地方,大家可以指出共同学习探讨)

    建议阅读的朋友自己也看过 seajs3.0 的源码,这样才不会觉得本文云里雾里。 或者直接到本文最后面下载 seajs3.0注释源码

    seajs的模块加载,是最核心、最复杂的一个模块了,作者把模块的状态分为了7种,实际上,最开始创建时模块状态为0,相当于有8种状态了。

    模块加载要解决的一个核心问题是依赖的问题,比如,我们使用入口方法: use('main.js'),  main.js 依赖 A和B,A依赖C,B依赖D,那么问题来了,什么时候可以执行     main.js中的内容呢?


    我们都知道,当A、B、C、D这四个模块加载并执行后便可以执行 main.js,而A、B、C、D的执行也是有顺序的,这一切都交由模块依赖管理来解决。
  
    模块会涉及到几个核心的方法:
    use   入口方法
    fetch 抓取模块js,实际上注册了onload回调事件,并在load中调用请求资源的接口去真正获取资源
    load  当一个模块加载并把元数据保存到cachedMods后会执行此方法(这时候模块已经知道依赖了哪些模块)
    pass  涉及到依赖处理最核心的 模块传递问题和remain计数
    exec  执行模块 factory
    define 即为模块js中的define方法
    save   保存模块meta data 到 cachedMods
    get   获取缓存中的Module实例


  
    下面讲一个小缩影看看一切是如何进行的:


    当执行 use('main.js')的时候, seajs创建了一个匿名模块(即Module实例,我们先称为初始模块),它默认已经加载好了,接着,它会调用它的load方法。load方法会遍历依赖的模块(即main模块),同时传递初始模块实例给main模块,初始模块实例有个remain变量,用于表示还差加载多少模块便可以执行了。


    到这里,remain的值显然为1。


    接着,初始模块的load方法会去fetch(main),然后获取 main.js,执行 main.js中的define方法,并解析main.js中的依赖A和B,当做完这一步之后,main模块的load方法得到了执行,它同样会把 初始模块实例 传递给 A和B,这时候remain为3,但是,这时候main模块已经执行完回调了,所以其实只要A和B模块都加载好了,便可以执行 初始模块了,所以最后 remain为2。


    接下来,同样去 fetch A和B,然后执行 A和B的 define,得到依赖C和D,同时把初始模块实例 传递给C和D,由于A和B回调也已经执行完毕,所以这时候 remain=2+2-2,依赖为2,只要C和D模块都加载好了,那么初始模块便可以执行。当C和D加载完成后,由于它们没有依赖了,所以不再需要传递 初始模块实例,当C加载完成后,由于C没有依赖,它会触发 onload方法,把remain变为1,D加载完成后,也没有依赖,它也会触发 onload方法,把 remain变为0,这时候一旦发现 remain=0,便会执行 use的时候注册的一个回调事件,表示可以执行匿名初始模块了
  
    其实,这时候,main、A、B、C、D模块中的 factory还没有真正得到执行,只是执行了define而已,use注册的回调会依次执行依赖模块的exec方法。
  
    整个流程最核心的就是对于 remain 的控制, 传递的 初始模块实例 存在 模块内部的_entry中,而 remain存储在初始模块实例中。


    整个模块依赖加载及处理最核心的理解的就是remain了,当remain=0,便会执行初始模块了,这时候所有依赖都解析好,加载好了,所以不需要考虑加载的问题,直接从main.js开始执行便是了,当遇到 require('A')的时候,会先执行A的factory得到exports,所以总体可以认为是 异步加载,但只有等到全部加载完之后才会去执行,除非使用了  require.async,相当于又使用了其它的 use。
  
    这里假定  A先于B加载, C先于D加载
    onRequest 为绑定在script节点上的onload事件,下图为一个简要的remain变量示意图
  



                                
    A和B在执行load方法前后,remain不变是由于A加载后,remain-1,但依赖了C,又加了1,B同理。                               
    C和D能够执行 Module.onload是由于它们都没有依赖。
    当D执行了 D.onload之后,发现remain为0,执行 匿名模块的callback,回调中开始依次执行各个模块,因为这时候所有模块都已经加载好了。                                  
 

    以上便为实现模块依赖加载的原理。

    最后附上笔者阅读源码时的一些注释,一份带注释的 seajs3.0 源码。 详见 : seajs3.0注释源码下载


0 0
原创粉丝点击