Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

seajs 阅读感悟 #1

Open
jyzwf opened this issue Jan 27, 2018 · 0 comments
Open

seajs 阅读感悟 #1

jyzwf opened this issue Jan 27, 2018 · 0 comments

Comments

@jyzwf
Copy link
Owner

jyzwf commented Jan 27, 2018

此文是自己对于阅读 Seajs 后的感悟,水平有限,如有不对或者不当,欢迎纠正 ^_^

前言

CMD 对于前端来说耳熟能详,而它是 SeaJS 在推广过程中对模块定义的规范化产出,此前一直对模块化比较新奇和疑惑,但没有细细研究,现在借 SeaJS 来解开自己心中的疑惑,虽说现在 webpack 等工具的兴起使 Seajs 走向了没落,但学习里面的思想还是很不错的

当然,本文并不会逐行分析,只是挑重点的来,重点就是 module.js 这个文件

进入正题

我们以以下代码为开端

// 加载一个模块,在加载完成时,执行回调
seajs.use('./A', function(a) {
  a.doSomething();
});


// A.js
define(function(require, exports, module) {
      // 获取模块 B 的接口
      var B = require('./B');

     // 调用模块 B 的方法
     B.doSomething();

      //获取模块 C 的接口
      var C = require('./C');

     // 调用模块 C 的方法
     C.doSomething();

    module.exports = {
       name: 'A',
       doSomething: function() {};
    };
});

// B.js
define(function(require, exports, module) {
      // 获取模块 E 的接口
      var E = require('./E');

     // 调用模块 E 的方法
     E.doSomething();

      //获取模块 F 的接口
      var F = require('./F');

     // 调用模块 F 的方法
     F.doSomething();

    module.exports = {
        name: 'B',
        doSomething: function() {};
    };

});

// E.js、F.js 、C.js 没有依赖,直接暴露接口
define(function(require, exports, module) {
  // 对外提供接口
  module.exports = {
    name: 'x',
    doSomething: function() {};
  };
});

依赖关系如下:
image

M 先会执行 load 方法去加载自己的依赖模块 A ,此时会把自己传给 A 的_entry数组(在pass方法里 ),并将自己的 _entry数组清空,便于下面代码的判断

// If module has entries not be passed, call onload
    // pass方法执行完成后,mod._entry存储被依赖的模块;当前模块加载完成时,执行被依赖模块的回调函数
    // 执行时机为,当依赖模块加载完成后,onload事件发生时,调用该依赖模块的load方法,执行被依赖模块的回调
    if (mod._entry.length) {
        mod.onload()
        return
    }

然后此时 if 不成立,执行后面通过添加 script 标签来加载 A 模块 , A 模块加载的同时,会执行A模块里面的 define 函数,实例化该模块 (调用 save 函数),收集该模块的一些信息,如该模块依赖的模块,模块名,factory 函数

image

A 模块加载好后调用其 load 方法,继续去加载 B、C 模块,注意,他又会在自己的 pass 方法中把 M 模块传给依赖模块,后面依次重复上述过程。
C、E、F每个模块执行 load 里的 pass 方法后,由于此时他们已经没有可以依赖的模块,他们 的 _entry数组里面是最初的 M 模块,回到 load 方法,上面的 if 语句就会执行,调用 onload 方法

image

也就是说,M 模块会被一直向后传递,直到末尾模块才停止,然后每个末尾模块加载好后会去判断是否所有的末尾模块都加载好了,如果是,就执行 M 模块的回调函数。

image

M 的回调函数会依次执行依赖模块的 exec 方法 ,把返回值按顺序传给回调函数使用。

回看每个模块的 exec 方法
这个方法就是将每个模块的 factory 函数进行执行,并将暴露的值返回出去。

总的来说,可以概括如下:
seajs 一开始就去加载各个模块,并在 define 中提取有用信息备用,然后,模块执行时 ,碰到require(‘xxx’),再去执行这个模块的 exec 方法,暴露结果供使用。

前面只是简单的讲了下 module 这个文件的流程,其中还有少可以学习的地方,还有其他文件,如 util-request.js是创建script 标签来请求文件的,util-path.js是用来分析路径的,util-deps.js是用来分析依赖的,等等,都可以学到很多知识。

至此 seajs 就讲完了,欢迎交流 ^_^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant