exports
首先是 exports 的几种形式,我们可以:
define(function(require, exports, module) { exports.a = 'a'; exports.fn = function() {}; });
也可以:
define(function(require, exports, module) { module.exports = { a: 'a', fn: function() {} }; });
这两者有什么区别呢?
- 就最终的效果而言,这两种形式没什么区别:都是使得该模块对外提供 xx 和 fn 两个公共成员。
- 就代码组织形式而言,
exports.xx
是分散赋值,中间可以穿插其他代码,很灵活。
= ...module.exports
则是集中式赋值,便于管理。两种形式各有优劣,采用何种方式,很大程度上取决于个人喜好。
= { ... } - 就内部实现而言,在传入参数时,
exports
, 很明显,第一种方式复用了
= module.exports = {}{}
初始值,第二种则给
module.exports 重新赋了一个新对象。由于require('module-id')
返回的是module.exports
,
因此最终的效果两种形式是一致的。 - 更直观的一种形式是
exports
, 但由于 JavaScript 里只能传值,不能传引用,直接给
= { ... }exports
赋值,外面无法获取到exports
的新值,因此才有了退而求其次的module.exports
形式。
= { ... }
模块书写格式与模块加载器
在 seajs 里,“加载”一个模块可以有:
seajs.use('a', callback); require('a'); module.load('a', callback);
首先, seajs.use
仅用来在页面中加载起始入口模块,这就如我们在
nodejs 里,运行模块的入口是:
node filename.js
seajs.use('a', callback)
就相当于 node
, 仅起到 bootstrap 的作用。
filename.js
在标准模块里,推荐永远不要出现 seajs. 就如 nodeJS 的模块里,不会出现 node 一样。标准模块的书写形式为:
define(function(require, exports, module) { // module code });
这样做的好处,可以使得模块和具体加载器无关。比如 SeaJS 能加载的模块,理论上用 RequireJS 也可以跑起来,因为遵循的模块书写规范有很大交集。CommonJS 社区的一个目标就是使得各种环境下,都遵循统一的模块书写格式。这样,SeaJS, RequireJS, nodeJS 等都只是模块加载器,只要遵循的模块书写规范一致,模块就可以通用。
动态加载
define(function(require, exports, module) { var a = require('a'); // ... module.load('b', function(b) { // ... }); // ... });
上面的代码, require('a')
对应的 a.js
模块在 require
执行前就已下载好,require('a')
仅执行
a.js 模块里 define 的 function 参数,以获取模块 a 的 exports. 这种方式是预先加载、按需执行。
而 module.load('b', callback)
是执行到此处时,才开始下载
b.js 模块。下载好后,回调 callback 函数。这种方式是动态延迟加载。
一般来说, require('a')
这种形式用于强依赖,缺了模块
a, 该模块功能就不全了。 module.load
则用于加载可选模块,比如在某些特定条件下才需要的功能。
这两种方式各自的使用场景不同,得具体问题具体分析。
有心人可以发现: module.load
和 seajs.use
是很类似的。内部实现上也的确是公用一套逻辑,唯一不同的是, module.load('path/to/module')
是相对当前模块来定位, seajs.use
是相对当前页面。
总结
目前的 API 设计,已尽量简化,尽量简明易懂。大家有什么好的想法和改进建议,非常欢迎反馈。(建议通过邮件或博客回复给我反馈,文档下面的 DISQUS 很慢,经常漏掉,不可靠。)