来看 RequireJS 的 API 页面:api.html, 该篇博客着重分析 RequireJS 和 SeaJS 在 ID 规则上的异同。

RequireJS 的 ID 规则

对于模块加载器来说,模块 id 遵守的规则非常重要。

首先来定义几个概念。假设当前页面路径为 http://t.com/a/b/c.html, 则:

  1. pageRoot = http://t.com
  2. pageUrl = http://t.com/a/b
  3. baseUrl: 在 RequireJS 里,默认情况下,baseUrl 就是 pageUrl, 除非:
    • 通过 require.config 更改了 baseUrl
    • 指定了 data-main, 比如
      <script src="require.js" data-main="scripts/main"></script> 
      

      这时 baseUrl 为 pageUrl/scripts

  4. paths: 表示路径的缩写,通过 config 来配置:
    require.config({ paths: { 'some': 'xxx/zzz' } })
    

    这时,some/a 实际上代表 xxx/zzz/a

  5. moduleUrl: 如果模块路径为 http://t.com/path/to/a.js, 则 moduleUrl = http://t.com/path/to

RequireJS 的 id 规则为:首先,会通过 paths 解析,解析完成后:

  1. 是 Page ID
    • 以 .js 结尾的,比如 some/a/b.js
    • 以 / 开头的,比如 /a/b
    • 以 http(s):// 开头的

    Page ID 的解析规则与 script src="xxx" 中的 xxx 一样。

  2. 是 some/module 这种 Top-Level ID, 会解析成 baseUrl/some/module.js
  3. 是 ./some/module 这种 Relative ID, 会解析成 moduleUrl/some/module.js

SeaJS 的 ID 规则

文档:module-identifier.html

SeaJS 的 ID 规则可以总结为:

    1. 省略约定.js 后缀可以省略。 require('path/to/a.js') 和 require('path/to/a') 是一样的。
  • 环境相关: 除了 Top-Level ID 始终相对 baseUrl 来定位,其他 ID 都是相对当前环境来定位。

举例如下:

// 在 http://t.com/test.html 中:
seajs.use(['./a', 'b', 'c.js', '/d', 'http://x.com/e']);

// 会下载
// http://t.com/a.js
// baseUrl/b.js
// baseUrl/c.js
// http://t.com/d.js
// http://x.com/e.js

// 在 http://cdn.com/path/to/t.js 中:
define(function(require) {
  require('./a');
  require('b');
  require('c.js');
  require('/d');
  require('http://x.com/e');
});

// 会下载
// http://cdn.com/path/to/a.js
// baseUrl/b.js
// baseUrl/c.js
// http://cdn.com/d.js
// http://x.com/e.js

 

可以看出,在 SeaJS 里,ID 可以分成两类:

  1. Top-Level IDsome/module 这种,会根据 baseUrl 来定位。
  2. Context ID: 除了 some/module 之外的所有形式,会根据当前环境来定位。

ID 规则对使用体验的影响

RequireJS 里有 Page ID 的概念,和浏览器解析 src 的规则保持一致。
SeaJS 里则有省缺 .js 后缀的约定,和 CommonJS 以及 NodeJS 的约定保持一致。

这个取舍,使得 RequireJS 的 ID 更接近文件路径,SeaJS 的 ID 则更接近模块标识。
这其实是由两者的定位决定的:RequireJS 想同时是文件和模块加载器,SeaJS 则只专注于模块加载器。

当采用 Simplified CommonJS Wrapper 格式时,RequireJS 和 SeaJS 的解析规则基本一致:

1
2
3
4
5
6
// xxx/to/a.js
define(function(require, exports, module) {
  require('a'); // baseUrl/a.js
  require('./b'); // xxx/to/b.js
  require('./b.js'); // xxx/to/b.js
});

RequireJS 的 ID 规则是比较容易让人混淆的,特别对于新手来说。有没有 .js 后缀,以及采用的模块书写格式,都会影响解析规则。

SeaJS 的 ID 规则更简单纯粹,和 CommonJS 以及 NodeJS 尽量保持一致。