决不轻言放弃,否则对不起自己

永不言败

导航

SeaJS 和 RequireJS 的异同

相同点是:要解决的问题相同,都是浏览器端的模块化开发,目标一致。

不同点有不少:

1. 遵循的规范不同

RequireJS 遵循的是 Modules/AMD 规范。

SeaJS 遵循的是 Mdoules/Wrappings 规范的 define 形式。

AMD 规范在 CommonJS 社区争议很大,规范里太多 RequireJS 的影子。社区里不少人反感 RequireJS 打着 CommonJS 的口号,甚至建议 AMD 自立门户,比如最近的这篇讨论:Split off AMD?

从这两个规范本身来说,Modules/Wrappings 规范更简洁优雅,不信的话,你读读它们各自的描述就清楚。

2. factory 的执行时机不同

在 RequireJS 里,模块有多种书写格式,推荐的是:

1
2
3
4
define(["./a", "./b"], function(a, b) {
  a.doSomething();
  b.doSomething();
});

在 SeaJS 里,模块只有一种书写格式:

1
2
3
4
define(function(require, exports, module) {
  require("./a").doSomething();
  require("./b").doSomething();
});

SeaJS 的模块书写格式,RequireJS 也支持。为了便于讨论,我们称呼 define 方法中的 function 参数为模块的 factory. 无论是在 RequireJS 还是 SeaJS 里,在执行 factory 之前,都会确保依赖的模块已经下载好:

1
2
3
4
5
6
7
8
/* a.js */
define(factory);
 
/* b.js */
define(factory);
 
/* c.js */
define(['./a', './b'], factory);

每个模块都有自己的 factory. 在 AMD 的书写格式下,上面例子中,模块 c 的 factory 在执行时,会接收 a 和 b 两个参数。这意味着,c 依赖的所有模块,都是在一开始就得执行好,即便有可能不需要执行,比如:

1
2
3
4
5
6
7
8
9
define(function(require) {
   // BEGIN
  if(some_condition) {
    require('./a').doSomething();
  } else {
    require('./b').soSomething();
  }
  // END
});

在 AMD 规范里,在 BEGIN 处,a 和 b 的 factory 都已经执行好。在 Wrappings 规范里,在 BEGIN 处,a 和 b 的 factory 还没未执行,在 END 处时,根据条件,只会执行其中一个。

可以看出,Wrappings 规范更“懒”,更节省 CPU. 更符合 nodejs 等环境下的使用习惯。

AMD 规范只所以采用提前执行,可以参考作者的说明:Standards and proposals for JavaScript Modules and jQuery, 提到了两点理由,但并不被社区认可。我个人觉得这是一种权衡,但是是一种糟糕的权衡,破坏了与 Modules/1.0 规范的和谐性。有兴趣的可以搜索 Google Groups, 有相当多的讨论。

Wrappings 规范则尽可能的保留了 Modules/1.0 规范中的习惯。权衡点在于,对于条件语句中 require:

1
2
3
4
5
if(true) {
  require('./a').doSomething();
} else {
  require('./b').soSomething();
}

在 node 等环境中,能直接同步读取文件,可以做到只加载和执行模块 a, 不加载模块 b. 但在浏览器端,要实现这点,很困难(可以通过类似 Jscex 的方式来实现,但复杂度急增)。面对现实,目前基本所有浏览器端的 loader, 都是把依赖的模块都下载下来。从打包部署的角度考虑,这样做并不会对性能造成影响,反而能让不同条件分支,共享同一缓存。Wrappings 规范的提前下载和延迟执行,是一种合理的权衡。

3. 设计理念不同

如果你用过 RequireJS, 会知道:require 方法极其灵活,比如下面这些:

1
2
3
4
require('a')  -- gets exports of module a
require(['a']) -- fetch module a according to module name scheme
require(['a.js'])  -- fetch a.js directly relative to current page
require({...})  -- set loader config

稍微有点变化,做的事情就不一样,个人很不喜欢,社区里也有不少人讨厌这种风格。

在 SeaJS 里,API 的设计理念是:

  • 保持简单,职责单一。
  • 遵守规范,但不拘泥。
  • 适度灵活。

SeaJS 的公共 API 只有 8 个:速查手册, 职责很清晰,简单明了。

不拘泥规范,大家可以看到,SeaJS 里并没有采用 Modules/Wrappings 规范里的 module.declare. 这是因为:

1
2
3
4
5
6
7
module.declare(function(require, exports, module) {
  module.id // 这是参数 module
});
 
module.declare(function(require, exports) {
  module.id // 这是全局 module
});

显然,module.declare 导致 module 变量和 this 一样,随着环境的变化而变化。对于新手来说,这会带来迷惑。不如直接引入 define 来得简洁明了。随着 RequireJS 的推广,大家对 define 的接受程度也更高,更容易被理解。还有一个好处:使得 SeaJS 的模块,理论上讲,可以直接运行于 RequireJS 上。

适度灵活,举个例子:

1
2
seajs.use("./a"); // 加载一个模块
seajs.use(["./a", "./b"]); // 加载多个模块

4. 聚焦点有差异

SeaJS 从一开始,到现在,都是 focus on web, 努力成为浏览器端的模块加载器。sea.js 源码里,只有和浏览器相关的代码,不像 requirejs 一样,牵三挂四,有为了能在 Rhino 和 node 下运行的代码,甚至还有为了与 jQuery 集成的代码。聚焦的好处之一是,能减少文件大小:

SeaJS 目前的大小是:3.2K (gzip)
RequireJS 的大小是:5.4K (gzip)

seajs 的源码,目前是分多个文件开发的,每个文件都很聚焦,有完善的测试用例。requirejs 目前依旧是一个大文件,不利于维护。

5. 最后,还是理念不一样

RequireJS 有一系列插件,功能很强大,但破坏了模块加载器的纯粹性,个人觉得不妥。

SeaJS 则努力保持简单,追求简洁之美。除非必要,勿增实体。在功能上,SeaJS 有个优势是,支持 CSS 模块的加载。RequireJS 因为技术原因,目前版本尚未实现。

最后的最后

根据实际需求选择就好,关键是要养成模块化开发的好习惯。

 

转自:http://lifesinger.wordpress.com/2011/05/17/the-difference-between-seajs-and-requirejs/(需FQ)

posted on 2013-04-16 15:23  XDS  阅读(464)  评论(0编辑  收藏  举报