这里是从sea.js 源码中的 Module.js 的Module核心代码抽离出来的,建议大家先行阅读
sea.js源码(Module.js核心代码),对本篇的理解会更加顺畅。
前提引入
前面我们讲到 seajs中,最后执行 顶部模块 的callback回调的时候,最后一层一层的向上执行依赖模块。而 其中的依赖模块是我们js文件中用Module.define()
定义的,相应就需要Module.exec()
去执行编译。
那么在这之前我们就先看一下 我们js代码如何定义模块。
1 | define(id,deps,function(require, exports, module) { |
这里我们都知道,function(){} 我们可以调用 require 加载资源,使用exports或module暴露模块接口。那么这些在seajs源码中怎么实现的呢?
实例前瞻
1 | <!--test.html--> |
seajs-js代码中模块的生成
- define(id,deps,factory) 方法,将js代码封装称为一个模块
1 | // Define a module |
- 熟悉seajs的同学一定知道
define()
定义模板的时候,有三个参数,(id ,deps, factory), 而id 和 deps是可选的,而这里对参数的处理。
1 | // define(factory) |
- 这里是解析 factory 中js代码中所需要 同步加载的模块,并赋值给 deps 依赖模块数组。
util-deps.js–parseDependencies() :解析factory代码中的require()加载的模块,而通过require.async()并不会被解析。
1
2
3
4// Parse dependencies according to the module factory code
if (!isArray(deps) && isFunction(factory)) {
deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString())
}根据定义,依赖,保存 ‘当前定义模块’ 到模块缓存中
1 | var meta = { |
seajs-js代码中模块的执行
Module.exec() 执行js代码,获取模块接口
1 | // Execute a module |
- 整体的思路就是
- 先判断状态,根据当前模块的状态进行处理。
- == 》 声明定义 require构造对象
- == 》 执行factory,将require构造对象,初始化的exports空对象,模块传入factory。
- == 》 暴露并返回exports
声明定义 require构造对象
1 | function require(id) { |
- 这里值得一提的就是
require.async
这是提供我们按需加载的接口。 - 先了解一下
require.async(id)
和require(id)
的不同- require(id) 返回模块执行解析的接口,这种同步依赖在Module.define()中调用parseDependencies函数解析得到需要的模块并添加到deps依赖数组中。
- 而require.async()请求的模块并不会,它会等到我们执行factory代码的时候,根据代码异步地按需加载,也就是说如果我们通过if()等没有执行该代码,则模块不会被加载进来。
- 当我们执行模块代码的时候,如果遇到require.async(),那么我们就会调用
Module.use(ids, callback, uri + "_async_" + cid())
,使用Module.use加载这个模块。从而达到异步加载。
执行factory,将(require构造对象,初始化的exports空对象,模块)传入factory
1 | factory(require, mod.exports = {}, mod) |
暴露exports接口
1 | return mod.exports |
总结
如何将一个js文件看成一个模块?
js文件 通过 define() 转换为 “模块”,并提供 require构造对象,exports接口对象,和 modul自身模块对象。
sea.js怎么实现按需加载?
通过提供
require.async()
,该方法引入的模块,不会在模块启动过程中被加载到页面,而是在我们执行解析模块代码的时候,自身调用Module.use()
才开始加载模块资源到页面和执行模块。