[摘要]前端模块化开发的价值
link
恼人的命名冲突
看不懂,也没必要深究历史,略过
烦琐的文件依赖
使用 Sea.js 来解决
使用 Sea.js,在书写文件时,需要遵守 CMD (Common Module Definition)模
块定义规范。
一个文件就是一个模块。
前面例子中的 util.js
1
2
3
4
5
6
7
8
9
10
11
| var org = {};
org.CoolSite = {};
org.CoolSite.Utils = {};
org.CoolSite.Utils.each = function (arr) {
// 实现代码
};
org.CoolSite.Utils.log = function (str) {
// 实现代码
};
|
变成:
1
2
3
4
5
6
7
8
9
| define(function(require, exports) {
exports.each = function (arr) {
// 实现代码
};
exports.log = function (str) {
// 实现代码
};
});
|
通过 exports 就可以向外提供接口。这样,dialog.js 的代码变成:
1
2
3
4
5
6
7
| define(function(require, exports) {
var util = require('./util.js');
exports.init = function() {
// 实现代码
};
});
|
所以: define API
关键部分到了!我们通过 require('./util.js') 就可以拿到 util.js 中通过 exports 暴露的接口。
这里的 require 可以认为是 Sea.js 给 JavaScript 语言增加的一个 语法关键字,通过 require 可以获取其他模块提供的接口。
类似于 css 中的:
1
2
3
4
| @import url("base.css");
#id { ... }
.class { ... }
|
Sea.js 增加的 require 语法关键字,就如 CSS 文件中的 @import 一样,给我们的源码赋予了依赖引入功能。
如果你是后端开发工程师,更不会陌生。Java、Python、C# 等等语言,都有 include、import 等功能。JavaScript 语言本身也有类似功能,但目前还处于草案阶段,需要等到 ES6 标准得到主流浏览器支持后才能使用。
这样,在页面中使用 dialog.js 将变得非常简单。
1
2
3
4
5
6
| <script src="sea.js"></script>
<script>
seajs.use('dialog', function(Dialog) {
Dialog.init(/* 传入配置 */);
});
</script>
|
首先要在页面中引入 sea.js 文件,这一般通过页头全局把控,也方便更新维护。
想在页面中使用某个组件时,只要通过 seajs.use 方法调用。
所以: seajs.use API
好好琢磨以上代码,我相信你已经看到了 Sea.js 带来的两大好处:
通过 exports 暴露接口。这意味着不需要命名空间了,更不需要全局变量。这是一种彻底的命名冲突解决方案。
通过 require 引入依赖。这可以让依赖内置,开发者只需关心当前模块的依赖,其他事情 Sea.js 都会自动处理好。对模块开发者来说,这是一种很好的 关注度分离,能让程序员更多地享受编码的乐趣。
[摘要] 5 分钟上手 Sea.js
目录结构
1
2
3
4
5
6
7
8
9
10
| examples/
|-- sea-modules 存放 seajs、jquery 等文件,这也是模块的部署目录
|-- static 存放各个项目的 js、css 文件
| |-- hello
| |-- lucky
| `-- todo
`-- app 存放 html 等文件
|-- hello.html
|-- lucky.html
`-- todo.html
|
我们从 hello.html 入手,来瞧瞧使用 Sea.js 如何组织代码。
在页面中加载模块
1
2
3
4
5
6
7
8
9
10
| // seajs 的简单配置
seajs.config({
base: "../sea-modules/",
alias: {
"jquery": "jquery/jquery/1.10.1/jquery.js"
}
})
// 加载入口模块
seajs.use("../static/hello/src/main")
|
seajs.config API
seajs.use API
模块代码
这个小游戏有两个模块 spinning.js 和 main.js,遵循统一的写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 所有模块都通过 define 来定义
define(function(require, exports, module) {
// 通过 require 引入依赖
var $ = require('jquery');
var Spinning = require('./spinning');
// 通过 exports 对外提供接口
exports.doSomething = ...
// 或者通过 module.exports 提供整个接口
module.exports = ...
});
|
[摘要] API 快速参考
seajs.config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| seajs.config({
// 设置路径,方便跨目录调用
paths: {
'arale': 'https://a.alipayobjects.com/arale',
'jquery': 'https://a.alipayobjects.com/jquery'
},
// 设置别名,方便调用
alias: {
'class': 'arale/class/1.0.0/class',
'jquery': 'jquery/jquery/1.10.1/jquery'
}
});
|
更多配置项请参考:#262
seajs.use
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 加载一个模块
seajs.use('./a');
// 加载一个模块,在加载完成时,执行回调
seajs.use('./a', function(a) {
a.doSomething();
});
// 加载多个模块,在加载完成时,执行回调
seajs.use(['./a', './b'], function(a, b) {
a.doSomething();
b.doSomething();
});
|
更多配置项请参考:#260
define
用来定义模块。Sea.js 推崇一个模块一个文件,遵循统一的写法:
1
2
3
4
5
| define(function(require, exports, module) {
// 模块代码
});
|
也可以手动指定模块 id 和依赖,详情请参考:#242
require, exports 和 module 三个参数可酌情省略,具体用法如下。
require
require 用来获取指定模块的接口。
1
2
3
4
5
6
7
8
| define(function(require) {
// 获取模块 a 的接口
var a = require('./a');
// 调用模块 a 的方法
a.doSomething();
});
|
注意,require 只接受字符串直接量作为参数,详细约定请阅读:#259
require.async
用来在模块内部异步加载一个或多个模块。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| define(function(require) {
// 异步加载一个模块,在加载完成时,执行回调
require.async('./b', function(b) {
b.doSomething();
});
// 异步加载多个模块,在加载完成时,执行回调
require.async(['./c', './d'], function(c, d) {
c.doSomething();
d.doSomething();
});
});
|
详细说明请参考:#242
exports
用来在模块内部对外提供接口。
1
2
3
4
5
6
7
8
9
| define(function(require, exports) {
// 对外提供 foo 属性
exports.foo = 'bar';
// 对外提供 doSomething 方法
exports.doSomething = function() {};
});
|
详细说明请参考:#242