seajs模块标识解析深度指南:手写ID与自动化构建避坑技巧
seajs官方文档对模块标识的说明已较为清晰,但并未涵盖所有实践细节。尤其是当开发者需要手动编写模块ID和依赖声明,或自行开发transport自动化工具时——往往spm并不完全适配,因为不同项目的目录结构差异悬殊,难以批量统一。因此,真正吃透ID解析规则,才能避免提升中的各种坑点。
首要牢记的几个核心原则:
- 顶级标识始终基于 base 基础路径进行解析。
- 绝对路径和根路径始终基于当前页面进行解析。
- require 和 require.async 中的相对路径基于当前模块所在路径解析。
- seajs.use 中的相对路径始终基于当前页面解析。

模块标识的三种类型详解
在seajs中,模块的ID主要分为三类:相对标识、顶级标识、普通路径。普通路径涵盖绝对路径、根路径等。这里重点说明前两种:
- 相对标识:以
./或../开头,例如./OtherModule、../lib/Base。 - 顶级标识:以文件或目录(包含字母、
-、_)开头,例如app/widget/Select。
模块ID需要填写的位置共有三处:
define("id (1)", ["../id2 (2)"], function(require, exports, module){
var moduleA = require('./moduleA (3)');
})
注意:无论是define的第一个参数【模块ID】、第二个参数【依赖模块的ID】,还是【require模块ID】,最终比对标准都是解析后的文件URI。因此这三处可以以任意形式书写,只要最终解析为同一个URI,即视为同一模块。在ID解析过程中,会预先经过 seajs.config 中 alias 和 paths 配置的处理。
base 路径的解析机制
base路径作为整个模块系统的基础,其自身解析规则独立(第1层,不依赖其他设置):
- base本身不能使用顶级标识,因为顶级标识是相对base解析的,所以base只能用相对标识或根路径。
- base默认路径为seajs所在目录,其他情况请参考官方文档。若不是seajs推荐的源码目录结构,建议手动设置base路径。
- 相对标识:相对于当前页面进行解析。
paths 中的路径解析规则
paths配置也属于第1层,自身路径不依赖其他设置。规则如下:
- 相对标识:在哪里被引用,相对的解析位置取决于被引用的上下文,遵循该上下文的规则。
- paths中的字段会作为变量在被使用的位置替换,然后再执行解析。
举例说明:
//config
seajs.config({
base:"./app/src",
path:{
"a":"../lib", //(1) 相对路径
"lib":"path/to/lib", //(2) 顶级标识
"l2":"/lib" //(3) 根路径
}
});
// 模块 mod/m/m.js:
require("a/jquery");
// => 转换为:"../../lib/jquery"
// => 加载:mod/lib/jquery (特别注意 1)
// 模块 mod/f.js:
require("a/jquery");
// => 转换为:"../../lib/jquery"
// => 加载:lib/jquery (特别注意 2)
可见,上述两个模块虽然使用了相同的 require("a/jquery"),但解析结果不同——原因在于模块所在路径深度不同,相对路径的解析基准自然有差异。
alias 中的路径解析规则
alias属于第2层,自身路径可以依赖于paths配置:
- alias的规则与paths类似,且alias路径也可以引用paths中的“变量”。
- 提示:在paths、alias中尽量使用顶级标识、根路径或绝对路径,避免使用相对标识,否则在不同深度的模块中引用时可能解析出不同的路径。
- 相对标识:在哪里被引用,相对的解析位置取决于被引用的上下文,遵循当地规则。
seajs.use 路径解析规则
相对标识:相对于当前页面进行解析。
define 定义模块 ID 的解析规则 (1)
(第3层,路径可相对于 alias 或 paths 设置)
可使用:相对标识、顶级标识、根路径。推荐使用顶级标识;若模块位置不在base基础路径内,则用相对标识或根路径。相对标识:相对于当前页面解析。
//config 使用上面代码块(1)的配置
// 模块1,无歧义,使用根路径解析
define("/app/src/module/Base", ..);
// 模块2,无歧义,使用顶级标识,相对于 base 基础路径解析
define("app/src/module/Base", ..);
// 模块3,有歧义,使用相对标识,此处相对于当前页面(引用该模块的HTML页面)
// 但其他位置即使使用表面相同的“ID”,也可能解析为不同模块
define("./app/src/module/Base",..);
模块依赖ID 的解析规则 (2)
(第3层,路径可相对于 alias 或 paths 设置)
相对标识:相对于 base 基础路径解析。
//config 使用上面代码块(1)的配置
//无歧义,相对于根路径解析
define("..", ["/app/src/module/Base"], ..)
// 无歧义,顶级标识,相对于 base 基础路径解析
define("..", ["app/src/module/Base"], ..)
//有歧义,相对标识,此处相对于当前模块解析
//此依赖看起来指向代码块(2)中的模块3
//但如果当前模块与当前页面不在同一层级目录,则不会被解析为模块3
define("..", ["./app/src/module/Base"],..)
模块内 require 其他模块 ID 的解析规则 (3)
(第3层,路径可相对于 alias 或 paths 设置)
相对标识:相对 base 基础路径解析
//config 使用上面代码块(1)的配置
define("..", [..], function(require){
//无歧义,相对于根路径解析
require("/app/src/module/Base");
});
define("..", [..], function(require){
// 无歧义,顶级标识,相对于 base 基础路径解析
require("app/src/module/Base");
});
define("..", [..], function(require){
//有歧义,相对标识,此处相对于当前模块解析
//此依赖看起来指向代码块(2)中的模块3
//但如果当前模块与当前页面不在同一层级目录,则不会被解析为模块3
require("./app/src/module/Base");
})
特别提醒:模块内三处需要写ID的位置,不必使用看起来完全相同的字符串,只要最终解析得到的模块一致即可。
总结与最佳实践
- paths 和 alias 的配置本质上是变量替换:在何处引用,就在该处替换为设定的值,再进行解析。
- 尽可能采用顶级标识,避免歧义。
- 若无法使用顶级标识(例如目录跨度较大),则优先设置 alias 或 paths,通过一个非相对路径的标识定位到目标目录,再在该标识下定义ID。
