Sea.js 入门教程系列之 Sea.js 介绍
为什么需要 Sea.js?—— 解决前端开发的痛点
在深入学习 Sea.js 之前,我们先来思考一个问题:为什么我们需要一个像 Sea.js 这样的工具?在没有模块化工具的时代,前端开发会遇到很多问题:

- 全局变量污染:所有的代码都运行在同一个作用域下,很容易因为变量名相同而引发冲突,一个库定义了一个
util对象,另一个库也定义了一个util对象,后加载的会覆盖前一个,导致程序出错。 - 依赖关系混乱:当项目变得庞大时,一个页面可能需要加载十几个甚至几十个 JS 文件,我们必须手动按照正确的顺序来加载这些文件,顺序错了就会导致报错,这种依赖关系的管理非常繁琐且容易出错。
- 代码复用困难:我们很难将一段功能独立的代码(比如一个工具函数、一个组件)封装起来,方便在其他项目中复用。
- 代码维护性差:所有代码都堆在一个或几个巨大的文件里,难以阅读、调试和维护。
为了解决这些问题,模块化开发应运而生,而 Sea.js 正是国内非常优秀的一个前端模块化加载器。
什么是 Sea.js?
Sea.js 是一个遵循 CMD (Common Module Definition) 规范的模块加载框架,由国内知名前端团队“玉伯”(现就职于阿里巴巴)及其团队开发。
Sea.js 帮助我们实现了以下几件事:
- 定义模块:提供了一套简单的语法,让我们可以用
define函数来定义一个模块。 - 模块标识:每个模块都有一个唯一的 ID(通常是文件路径),方便引用。
- 管理依赖:通过
require函数来声明模块的依赖关系,Sea.js 会在编译时自动分析这些依赖,并按正确的顺序加载所需的 JS 文件。 - 作用域隔离:每个模块的代码都运行在独立的作用域中,完美解决了全局变量污染的问题。
它的核心目标是:让前端模块化开发变得简单、优雅。

Sea.js 的核心概念
要理解 Sea.js,需要先了解几个核心概念:
a. 模块
模块是 Sea.js 的基本单位,一个模块就是一个独立的 JS 文件,它实现了特定的功能,并可以被其他模块引用。
在 Sea.js 中,我们使用 define 函数来定义一个模块。
b. define 函数
define 是定义模块的唯一接口,它接受一个函数作为参数,这个函数就是模块的工厂函数。

// define(id?, deps?, factory);
id(可选): 模块的唯一标识,通常我们省略它,Sea.js 会根据文件路径自动生成。deps(可选): 模块依赖的模块 ID 列表,通常我们也省略它,Sea.js 会自动从factory的require调用中分析出依赖。factory: 模块的工厂函数,它可以是函数,也可以是对象、字符串等,当是函数时,它的返回值就是模块的导出内容。
一个简单的模块示例:
创建一个文件 math.js,它提供了两个简单的数学工具函数。
// math.js
define(function(require, exports, module) {
// 在模块内部定义私有变量,不会污染全局作用域
var add = function(a, b) {
return a + b;
};
var multiply = function(a, b) {
return a * b;
};
// 通过 exports 对象向外暴露接口
exports.add = add;
exports.multiply = multiply;
});
说明:
define内部的require,exports,module是由 Sea.js 注入的,我们不需要自己定义。exports是一个对象,我们通过给它添加属性(如exports.add)来向外部暴露接口。- 在这个模块中,
add和multiply是私有变量,外部无法直接访问。
c. require 函数
require 是用来获取其他模块所提供接口的函数,它会告诉 Sea.js 需要加载哪个模块,并等待加载完成后,执行回调函数。
// require(id)
id: 你想要加载的模块的 ID(通常是相对于当前文件的路径)。
一个使用 require 的示例:
创建一个文件 app.js,它需要使用 math.js 中的功能。
// app.js
define(function(require, exports, module) {
// 使用 require 加载 math.js 模块
var math = require('./math.js');
// 现在可以使用 math 模块暴露出来的方法了
var sum = math.add(1, 2);
var product = math.multiply(3, 4);
console.log('1 + 2 =', sum); // 输出: 1 + 2 = 3
console.log('3 * 4 =', product); // 输出: 3 * 4 = 12
});
d. module 对象
module 对象代表当前模块本身,它包含了一些有用的信息。
module.id: 当前模块的 ID。module.uri: 当前模块的绝对路径。module.exports: 模块的最终导出对象,它与exports指向同一个对象,在某些高级场景下,你可能需要直接修改module.exports(导出一个构造函数)。
Sea.js vs. Require.js
Sea.js 和 Require.js 都是前端模块化加载器,它们都遵循 CommonJS 规范的变体(Sea.js 是 CMD,Require.js 是 AMD),它们的主要区别在于模块加载和执行的策略上:
| 特性 | Sea.js (CMD) | Require.js (AMD) |
|---|---|---|
| 规范 | CMD (Common Module Definition) | AMD (Asynchronous Module Definition) |
| 加载策略 | 按需加载,就近依赖,模块代码在 require 时才执行。 |
提前加载,异步并行,所有依赖模块会预先并行加载,然后在回调中执行。 |
| 代码风格 | 代码更接近 Node.js 风格,依赖声明在代码内部,清晰直观。 | 依赖需要在 define 的参数中明确列出,代码在顶部。 |
| 适用场景 | 适合需要按需加载、对性能有极致追求的场景,特别是移动端。 | 适合需要提前加载所有依赖、模块间关系固定的场景。 |
通俗比喻:
- Require.js (AMD) 像是 西餐点餐:你先把所有菜(依赖)都告诉服务员,然后厨房开始准备,所有菜都齐了,服务员再一起端上来(执行回调)。
- Sea.js (CMD) 像是 中餐点餐:你先点一个主菜(
require一个模块),等主菜上来了,吃着吃着发现需要配个醋,你再点一份醋(require另一个模块),依赖关系是动态、就近的。
- Sea.js 是什么? 一个遵循 CMD 规范的前端模块加载器。
- 它能解决什么问题? 解决了全局污染、依赖混乱、代码复用和维护性差等问题。
- 核心概念是什么?
- 模块:独立的 JS 文件。
define:定义模块的函数。require:加载依赖模块的函数。exports:向外暴露接口的对象。
- 它的主要特点是什么? 按需加载、就近依赖,代码风格更自然。
通过本篇介绍,相信你已经对 Sea.js 有了一个基本的认识,它是一个非常优雅和强大的工具,能极大地提升我们的前端开发体验和项目质量。
在下一篇教程中,我们将通过一个完整的实例,手把手教你如何搭建一个基于 Sea.js 的项目,并编写你的第一个模块,敬请期待!
