SeaJS 是由玉伯开发的一款严格遵循 CommonJS 规范的模块加载器,主要用于高效、干净地加载 JavaScript 模块和 CSS 样式文件。这个工具体积极小——压缩并经过 gzip 处理后仅 4KB,提供的接口和方法也极为精简:核心功能只有模块定义、模块加载以及依赖管理。然而,它功能强大,能够确保在使用某个模块时,所有依赖模块已经稳定加载到环境中。玉伯曾非常务实地说过:SeaJS 能让你真正享受编码的乐趣,再也不用为脚本加载顺序而烦恼。
说实话,你现在还在一个页面里一口气引用几十个 JS 和 CSS 文件吗?我统计过自己公司网站的个人主页首页,竟然引用了多达 39 个 CSS 和 JS 文件。这带来的问题显而易见:

- 维护成本急剧上升,前端和后端都感到头疼。
- HTTP 请求数量过多——虽然可以用合并工具解决,但如果没有后端支持,人工成本很高;即使后端帮助合并,那么长的路径字符串也让人眼花缭乱。
SeaJS 正是解决这些痛点的理想工具。
模块的定义 define
定义一个模块非常简单。例如,创建一个 sayHello.js 文件:
复制代码 代码如下:
define(function(require, exports, module) {
exports.sayHello = function(eleID, text) {
document.getElementById(eleID).innerHTML = text;
};
});
这里需要重点关注 exports 参数,它是模块对外暴露 API 的通道。其他模块通过 exports.sayHello 即可调用到该方法。
模块的加载 use
如果页面上有一个 id 为 out 的元素,想要输出 "Hello SeaJS!",操作步骤如下:
- 先引入
sea.js - 使用
seajs.use加载sayHello模块:
复制代码 代码如下:
seajs.use("sayHello/sayHello", function(say) {
say.sayHello("out", "Hello SeaJS!");
});
seajs.use 是加载模块的核心方法:
- 第一个参数是模块标识,路径相对于
sea.js所在位置,.js后缀可以省略。当然,模块标识还有多种写法,具体可参考官方文档(https://seajs.com/docs/zh-cn/module-identifier.html)。 - 第二个参数为回调函数,参数
say即加载进来的sayHello模块。通过say.sayHello()调用模块暴露的方法。
模块的依赖关系
模块之间的依赖关系应该在定义时就规划清楚。比如将上面的 sayHello 模块改造一下——假设我们已经有一个通用的 DOM 工具模块(包含获取元素、设置样式等常用方法),例如这样的 DOM.js:
复制代码 代码如下:
define(function(require, exports, module) {
var DOM = {
/**
* 通过元素的id属性获取DOM对象,参数为字符串,或多个字符串
* @id getById
* @method getById
* @param {String} id the id attribute
* @return {HTMLElement | Object} The HTMLElement with the id, or null if none found.
*/
getById: function() {
var els = [];
for (var i = 0; i < arguments.length; i++) {
var el = arguments[i];
if (typeof el == "string") {
el = document.getElementById(el);
}
if (arguments.length == 1) {
return el;
}
els.push(el);
}
return els;
},
/**
* get 获取对象,可以传入对象或字符串,如果传入字符串则以document.getElementById()的方式获取对象
* @id get
* @param {String} el html element
* @return {Object} HTMLElement object.
*/
get: function(el) {
if (el & (el.tagName || el.item)) {
return el;
}
return this.getById(el);
}
};
return DOM;
});
那么 sayHelloA 模块可以这样编写(新建 sayHelloA.js):
复制代码 代码如下:
define(function(require, exports, module) {
var DOM = require("DOM/DOM");
require("sayHelloA/sayHello.css");
exports.sayHello = function(eleID, text) {
DOM.get(eleID).innerHTML = text;
};
});
require 函数的作用就是建立模块之间的依赖关系。上面的 sayHelloA 依赖于 DOM 模块(因为用到了 DOM.get),通过 var DOM = require("DOM/DOM") 加载并赋值;require("sayHelloA/sayHello.css") 则直接引入了 CSS 文件——这样页面就会自动加载该样式。
最近我一直在深入研究 SeaJS,越是深入了解就越喜欢它。感谢玉伯!当然,你可能会觉得这些简单的示例没必要大费周章。确实,项目规模小、JS 文件少的时候,模块化的优势不那么明显;但一旦文件数量增加,或者项目达到中型以上规模,模块化带来的清晰结构与良好的可维护性就会充分体现出来。
