[toc]
seajs.config
============
用来对 Sea.js 进行配置。
```
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'
}
});
```
更多配置项请参考:
配置
--
可以对 Sea.js 进行配置,让模块编写、开发调试更方便。
---
seajs.config `seajs.config(options)`
------------------------------------
用来进行配置的方法。
~~~
seajs.config({
// 别名配置
alias: {
'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
'json': 'gallery/json/1.0.2/json',
'jquery': 'jquery/jquery/1.10.1/jquery'
},
// 路径配置
paths: {
'gallery': 'https://a.alipayobjects.com/gallery'
},
// 变量配置
vars: {
'locale': 'zh-cn'
},
// 映射配置
map: [
['http://example.com/js/app/', 'http://localhost/js/app/']
],
// 预加载项
preload: [
Function.prototype.bind ? '' : 'es5-safe',
this.JSON ? '' : 'json'
],
// 调试模式
debug: true,
// Sea.js 的基础路径
base: 'http://example.com/path/to/base/',
// 文件编码
charset: 'utf-8'
});
~~~
支持以下配置选项:
### alias `Object`
当模块标识很长时,可以使用 `alias` 来简化。
~~~
seajs.config({
alias: {
'jquery': 'jquery/jquery/1.10.1/jquery',
'app/biz': 'http://path/to/app/biz.js',
}
});
~~~
~~~
define(function(require, exports, module) {
var $ = require('jquery');
//=> 加载的是 http://path/to/base/jquery/jquery/1.10.1/jquery.js
var biz = require('app/biz');
//=> 加载的是 http://path/to/app/biz.js
});
~~~
使用 `alias`,可以让文件的真实路径与调用标识分开,有利于统一维护。
### paths `Object`
当目录比较深,或需要跨目录调用模块时,可以使用 `paths` 来简化书写。
~~~
seajs.config({
paths: {
'gallery': 'https://a.alipayobjects.com/gallery',
'app': 'path/to/app',
}
});
~~~
~~~
define(function(require, exports, module) {
var underscore = require('gallery/underscore');
//=> 加载的是 https://a.alipayobjects.com/gallery/underscore.js
var biz = require('app/biz');
//=> 加载的是 path/to/app/biz.js
});
~~~
`paths` 配置可以结合 `alias` 配置一起使用,让模块引用非常方便。
### vars `Object`
有些场景下,模块路径在运行时才能确定,这时可以使用 `vars` 变量来配置。
~~~
seajs.config({
vars: {
'locale': 'zh-cn'
}
});
~~~
~~~
define(function(require, exports, module) {
var lang = require('./i18n/{locale}.js');
//=> 加载的是 path/to/i18n/zh-cn.js
});
~~~
`vars` 配置的是模块标识中的变量值,在模块标识中用 `{key}` 来表示变量。
### map `Array`
该配置可对模块路径进行映射修改,可用于路径转换、在线调试等。
~~~
seajs.config({
map: [
[ '.js', '-debug.js' ]
]
});
~~~
~~~
define(function(require, exports, module) {
var a = require('./a');
//=> 加载的是 path/to/a-debug.js
});
~~~
##Sea.js 的调试接口
使用 Sea.js,无论开发时还是上线后,调试都很方便。下面一一阐述。
seajs.cache Object
通过 seajs.cache,可以查阅当前模块系统中的所有模块信息。
比如,打开 seajs.org,然后在 WebKit Developer Tools 的 Console 面板中输入 seajs.cache,可以看到:
~~~
Object
> http://seajs.org/docs/assets/main.js: x
> https://a.alipayobjects.com/jquery/jquery/1.10.1/jquery.js: x
> __proto__: Object
~~~
这些就是文档首页用到的模块。展开某一项可以看到模块的具体信息,含义可参考:CMD 模块定义规范 中的 module 小节。
`seajs.resolve Function`
类似 require.resolve,会利用模块系统的内部机制对传入的字符串参数进行路径解析。
~~~
seajs.resolve('jquery');
// => http://path/to/jquery.js
seajs.resolve('./a', 'http://example.com/to/b.js');
// => http://example.com/to/a.js
~~~
`seajs.resolve`方法不光可以用来调试路径解析是否正确,还可以用在插件开发环境中。
###seajs.require Function
全局的 `require` 方法,可用来直接获取模块接口,比如
~~~
seajs.use(['a', 'b'], function() {
var a = seajs.require('a')
var b = seajs.require('b')
// do something...
})
~~~
###seajs.data Object
通过 seajs.data,可以查看 seajs 所有配置以及一些内部变量的值,可用于插件开发。当加载遇到问题时,也可用于调试。
### preload `Array`
使用 `preload` 配置项,可以在普通模块加载前,提前加载并初始化好指定模块。
~~~
// 在老浏览器中,提前加载好 ES5 和 json 模块
seajs.config({
preload: [
Function.prototype.bind ? '' : 'es5-safe',
this.JSON ? '' : 'json'
]
});
~~~
`preload` 中的空字符串会被忽略掉。
**注意**:`preload` 中的配置,需要等到 `use` 时才加载。比如:
~~~
seajs.config({
preload: 'a'
});
// 在加载 b 之前,会确保模块 a 已经加载并执行好
seajs.use('./b');
~~~
`preload` 配置不能放在模块文件里面:
~~~
seajs.config({
preload: 'a'
});
define(function(require, exports) {
// 此处执行时,不能保证模块 a 已经加载并执行好
});
~~~
### debug `Boolean`
值为 `true` 时,加载器不会删除动态插入的 script 标签。插件也可以根据 debug 配置,来决策 log 等信息的输出。
### base `String`
Sea.js 在解析顶级标识时,会相对 `base` 路径来解析。详情请参阅 [模块标识](https://github.com/seajs/seajs/issues/258)
**在 `seajs@2.3.0` 之前,Sea.js 会根据 sea.js 的路径去猜测 `base` 路径,一般为路径上含有 seajs 字符串的上一级路径。在 `seajs@2.3.0` 后,去掉了这个模糊的猜测。我们推荐始终手动设置一个准确的 `base` 路径**。
### charset `String | Function`
获取模块文件时,`<script>` 或 `<link>` 标签的 `charset` 属性。 默认是 `utf-8`
`charset` 还可以是一个函数:
~~~
seajs.config({
charset: function(url) {
// xxx 目录下的文件用 gbk 编码加载
if (url.indexOf('http://example.com/js/xxx') === 0) {
return 'gbk';
}
// 其他文件用 utf-8 编码
return 'utf-8';
}
});
~~~
提示
--
### 多次配置自动合并
`seajs.config` 可以多次运行,每次运行时,会对配置项进行合并操作:
~~~
seajs.config({
alias: {
'jquery': 'path/to/jquery.js',
'a': 'path/to/a.js'
},
preload: ['seajs-text']
});
seajs.config({
alias: {
'underscore': 'path/to/underscore.js',
'a': 'path/to/biz/a.js'
},
preload: ['seajs-combo']
});
~~~
上面两处 `config` 运行的结果是:
~~~
alias = {
'jquery': 'path/to/jquery.js',
'underscore': 'path/to/underscore.js',
'a': 'path/to/biz/a.js'
};
preload = ['seajs-text', 'seajs-combo'];
~~~
即:`config` 会自动合并不存在的项,对存在的项则进行覆盖。
### 插件的配置
插件可以给 `Sea.js` 添加配置项,请查看具体插件了解相关配置。
### 配置文件
配置可以直接写在 html 页面上,也可以独立出来成为一个文件。
~~~
config.js
seajs.config({
...
});
~~~
独立成一个文件时,一般通过 script 标签在页面中同步引入。
---
常用的配置项是 `alias`、`paths`、`base`,其他配置项有需要时,来查查文档就会用了。
seajs.use
=========
用来在页面中加载一个或多个模块。
~~~
// 加载一个模块
seajs.use('./a');
// 加载一个模块,在加载完成时,执行回调
seajs.use('./a', function(a) {
a.doSomething();
});
// 加载多个模块,在加载完成时,执行回调
seajs.use(['./a', './b'], function(a, b) {
a.doSomething();
b.doSomething();
});
~~~
更多用法请参考:
模块的加载启动
-------
Sea.js 是一个模块加载器,模块加载器需要实现两个基本功能:
1. 实现模块定义规范,这是模块系统的基础。
2. 模块系统的启动与运行。
### 模块定义规范的实现
这就是 `define`,`require`,`exports`,`module` 的实现。具体实现细节,有兴趣的可以看 Sea.js 的源码:[seajs/src](https://github.com/seajs/seajs/tree/master/src)。可以按照 [Gruntfile.js](https://github.com/seajs/seatools/blob/master/Gruntfile.js#L98) 中声明的合并顺序阅读,核心是 `module.js` 文件。
`define` 等方法的具体使用,请阅读:[CMD 模块定义规范](https://github.com/seajs/seajs/issues/242)
### 模块系统的启动
有了 `define` 等模块定义规范的实现,我们可以开发出很多模块。但光有一堆模块不管用,我们还得让它们能跑起来。
首先就是启动问题。比如在 Node 中,启动很简单:
```
$ node main.js
```
这就是启动。
再举一个例子,操作系统的启动:大家都知道的,按一下开机键就好。
在 Sea.js 里,要启动模块系统很简单:
~~~
<script src="path/to/sea.js"></script>
<script>
seajs.use('./main');
</script>
~~~
seajs.use `Function`
--------------------
用来在页面中加载模块。
### seajs.use `seajs.use(id, callback?)`
通过 `use` 方法,可以在页面中加载任意模块:
~~~
// 加载模块 main,并在加载完成时,执行指定回调
seajs.use('./main', function(main) {
main.init();
});
~~~
`use` 方法还可以一次加载多个模块:
~~~
// 并发加载模块 a 和模块 b,并在都加载完成时,执行指定回调
seajs.use(['./a', './b'], function(a, b) {
a.init();
b.init();
});
~~~
`callback` 参数可选,省略时,表示无需回调。
### 与 DOM ready 的关系
**注意**:`seajs.use` 与 `DOM ready` 事件没有任何关系。如果某些操作要确保在 `DOM ready` 后执行,需要使用`jquery` 等类库来保证,比如:
~~~
seajs.use(['jquery', './main'], function($, main) {
$(document).ready(function() {
main.init();
});
});
~~~
sea.js 的引入
----------
在调用 `seajs.use` 之前,需要先引入 `sea.js` 文件,推荐直接使用 `script` 标签同步引入:
```
<script src="path/to/sea.js"></script>
```
为了满足某些场景下的性能优化需求,也可以将 `sea.js` 的源码内嵌:
```
<script>
// sea.js 的源码
</script>
```
注意:代码内嵌时,需要通过 `seajs.config` 手动配置 `base` 路径。
最佳实践
----
1. `seajs.use` 理论上只用于加载启动,不应该出现在 `define` 中的模块代码里。在模块代码里需要异步加载其他模块时,推荐使用 `require.async` 方法。
2. 引入 `sea.js` 时,可以把 `sea.js` 与其他文件打包在一起,可提前合并好,或利用 combo 服务动态合并。无论哪一种方式,为了让 `sea.js` 内部能快速获取到自身路径,推荐手动加上 `id` 属性:
```
<script src="path/to/sea.js" id="seajsnode"></script>
```
加上 `seajsnode` 值,可以让 `sea.js` 直接获取到自身路径,而不需要通过其他机制去自动获取。这对性能和稳定性会有一定提升,推荐默认都加上。
小结
--
`seajs.use` 是模块加载器必备的一个接口。在 `seajs` 上,还有用于配置的 `config` 方法、方便调试的`cache` 等接口,这些会在接下来的文档中详细阐述。
define
======
用来定义模块。Sea.js 推崇一个模块一个文件,遵循统一的写法:
~~~
define(function(require, exports, module) {
// 模块代码
});
~~~
require.async
=============
用来在模块内部异步加载一个或多个模块。
~~~
define(function(require) {
// 异步加载一个模块,在加载完成时,执行回调
require.async('./b', function(b) {
b.doSomething();
});
// 异步加载多个模块,在加载完成时,执行回调
require.async(['./c', './d'], function(c, d) {
c.doSomething();
d.doSomething();
});
});
~~~
exports
=======
用来在模块内部对外提供接口。
~~~
define(function(require, exports) {
// 对外提供 foo 属性
exports.foo = 'bar';
// 对外提供 doSomething 方法
exports.doSomething = function() {};
});
~~~
module.exports
==============
与 `exports` 类似,用来在模块内部对外提供接口。
~~~
define(function(require, exports, module) {
// 对外提供接口
module.exports = {
name: 'a',
doSomething: function() {};
};
});
~~~
CMD 模块定义规范
----------
在 Sea.js 中,所有 JavaScript 模块都遵循 CMD([Common Module Definition](https://github.com/cmdjs/specification/blob/master/draft/module.md)) 模块定义规范。该规范明确了模块的基本书写格式和基本交互规则。
在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:
```
define(factory);
```
define `Function`
-----------------
`define` 是一个全局函数,用来定义模块。
### define `define(factory)`
`define` 接受 `factory` 参数,`factory` 可以是一个函数,也可以是一个对象或字符串。
`factory` 为对象、字符串时,表示模块的接口就是该对象、字符串。比如可以如下定义一个 JSON 数据模块:
```
define({ "foo": "bar" });
```
也可以通过字符串定义模板模块:
```
define('I am a template. My name is {{name}}.');
```
`factory` 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。`factory` 方法在执行时,默认会传入三个参数:`require`、`exports` 和 `module`:
~~~
define(function(require, exports, module) {
// 模块代码
});
~~~
### define `define(id?, deps?, factory)`
`define` 也可以接受两个以上参数。字符串 `id` 表示模块标识,数组 `deps` 是模块依赖。比如:
~~~
define('hello', ['jquery'], function(require, exports, module) {
// 模块代码
});
~~~
`id` 和 `deps` 参数可以省略。省略时,可以通过构建工具自动生成。
**注意**:带 `id` 和 `deps` 参数的 `define` 用法不属于 CMD 规范,而属于 [Modules/Transport](https://github.com/cmdjs/specification/blob/master/draft/transport.md) 规范。
### define.cmd `Object`
一个空对象,可用来判定当前页面是否有 CMD 模块加载器:
~~~
if (typeof define === "function" && define.cmd) {
// 有 Sea.js 等 CMD 模块加载器存在
}
~~~
require `Function`
------------------
`require` 是 `factory` 函数的第一个参数。
### require `require(id)`
`require` 是一个方法,接受 [模块标识](https://github.com/seajs/seajs/issues/258) 作为唯一参数,用来获取其他模块提供的接口。
~~~
define(function(require, exports) {
// 获取模块 a 的接口
var a = require('./a');
// 调用模块 a 的方法
a.doSomething();
});
~~~
**注意**:在开发时,`require` 的书写需要遵循一些 [简单约定](https://github.com/seajs/seajs/issues/259)。
### require.async `require.async(id, callback?)`
`require.async` 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。`callback` 参数可选。
~~~
define(function(require, exports, module) {
// 异步加载一个模块,在加载完成时,执行回调
require.async('./b', function(b) {
b.doSomething();
});
// 异步加载多个模块,在加载完成时,执行回调
require.async(['./c', './d'], function(c, d) {
c.doSomething();
d.doSomething();
});
});
~~~
**注意**:`require` 是同步往下执行,`require.async` 则是异步回调执行。`require.async` 一般用来加载可延迟异步加载的模块。
### require.resolve `require.resolve(id)`
使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。
~~~
define(function(require, exports) {
console.log(require.resolve('./b'));
// ==> http://example.com/path/to/b.js
});
~~~
这可以用来获取模块路径,一般用在插件环境或需动态拼接模块路径的场景下。
exports `Object`
----------------
`exports` 是一个对象,用来向外提供模块接口。
~~~
define(function(require, exports) {
// 对外提供 foo 属性
exports.foo = 'bar';
// 对外提供 doSomething 方法
exports.doSomething = function() {};
});
~~~
除了给 `exports` 对象增加成员,还可以使用 `return` 直接向外提供接口。
~~~
define(function(require) {
// 通过 return 直接提供接口
return {
foo: 'bar',
doSomething: function() {}
};
});
~~~
如果 `return` 语句是模块中的唯一代码,还可简化为:
~~~
define({
foo: 'bar',
doSomething: function() {}
});
~~~
上面这种格式特别适合定义 JSONP 模块。
**特别注意**:下面这种写法是错误的!
~~~
define(function(require, exports) {
// 错误用法!!!
exports = {
foo: 'bar',
doSomething: function() {}
};
});
~~~
正确的写法是用 `return` 或者给 `module.exports` 赋值:
~~~
define(function(require, exports, module) {
// 正确写法
module.exports = {
foo: 'bar',
doSomething: function() {}
};
});
~~~
**提示**:`exports` 仅仅是 `module.exports` 的一个引用。在 `factory` 内部给 `exports` 重新赋值时,并不会改变 `module.exports` 的值。因此给 `exports` 赋值是无效的,不能用来更改模块接口。
module `Object`
---------------
`module` 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
### module.id `String`
模块的唯一标识。
~~~
define('id', [], function(require, exports, module) {
// 模块代码
});
~~~
上面代码中,`define` 的第一个参数就是模块标识。
### module.uri `String`
根据模块系统的路径解析规则得到的模块绝对路径。
~~~
define(function(require, exports, module) {
console.log(module.uri);
// ==> http://example.com/path/to/this/file.js
});
~~~
一般情况下(没有在 `define` 中手写 `id` 参数时),`module.id` 的值就是 `module.uri`,两者完全相同。
### module.dependencies `Array`
`dependencies` 是一个数组,表示当前模块的依赖。
### module.exports `Object`
当前模块对外提供的接口。
传给 `factory` 构造方法的 `exports` 参数是 `module.exports` 对象的一个引用。只通过 `exports` 参数来提供接口,有时无法满足开发者的所有需求。 比如当模块的接口是某个类的实例时,需要通过 `module.exports`来实现:
~~~
define(function(require, exports, module) {
// exports 是 module.exports 的一个引用
console.log(module.exports === exports); // true
// 重新给 module.exports 赋值
module.exports = new SomeClass();
// exports 不再等于 module.exports
console.log(module.exports === exports); // false
});
~~~
**注意**:对 `module.exports` 的赋值需要同步执行,不能放在回调函数里。下面这样是不行的:
~~~
// x.js
define(function(require, exports, module) {
// 错误用法
setTimeout(function() {
module.exports = { a: "hello" };
}, 0);
});
~~~
在 y.js 里有调用到上面的 x.js:
~~~
// y.js
define(function(require, exports, module) {
var x = require('./x');
// 无法立刻得到模块 x 的属性 a
console.log(x.a); // undefined
});
~~~
小结
--
这就是 CMD 模块定义规范的所有内容。经常使用的 API 只有 `define`, `require`, `require.async`, `exports`,`module.exports` 这五个。其他 API 有个印象就好,在需要时再来查文档,不用刻意去记。
与 RequireJS 的 AMD 规范相比,CMD 规范尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。通过 CMD 规范书写的模块,可以很容易在 Node.js 中运行,后续会介绍。
`require`, `exports` 和 `module` 三个参数可酌情省略,具体用法如下。
require
=======
`require` 用来获取指定模块的接口。
~~~
define(function(require) {
// 获取模块 a 的接口
var a = require('./a');
// 调用模块 a 的方法
a.doSomething();
});
~~~
注意,`require` 只接受字符串直接量作为参数,详细约定请阅读:
require 书写约定
------------
使用 Sea.js 书写模块代码时,需要遵循一些简单规则。
> 只是书写和调试时的规范!!!构建后的代码完全不需要遵循下面的约定!!!!!!
### 1. 正确拼写
模块 factory 构造方法的第一个参数 **必须** 命名为 `require` 。
~~~
// 错误!
define(function(req) {
// ...
});
// 正确!
define(function(require) {
// ...
});
~~~
### 2. 不要修改
不要重命名 `require` 函数,或在任何作用域中给 `require` 重新赋值。
~~~
// 错误 - 重命名 "require"!
var req = require, mod = req("./mod");
// 错误 - 重定义 "require"!
require = function() {};
// 错误 - 重定义 "require" 为函数参数!
function F(require) {}
// 错误 - 在内嵌作用域内重定义了 "require"!
function F() {
var require = function() {};
}
~~~
### 3. 使用直接量
`require` 的参数值 **必须** 是字符串直接量。
~~~
// 错误!
require(myModule);
// 错误!
require("my-" + "module");
// 错误!
require("MY-MODULE".toLowerCase());
// 正确!
require("my-module");
~~~
在书写模块代码时,必须遵循这些规则。其实只要把 `require` **看做是语法关键字** 就好啦。
关于动态依赖
------
有时会希望可以使用 `require` 来进行条件加载:
~~~
if (todayIsWeekend)
require("play");
else
require("work");
~~~
但请牢记,从静态分析的角度来看,这个模块同时依赖 play 和 work 两个模块,加载器会把这两个模块文件都下载下来。 这种情况下,推荐使用 `require.async` 来进行条件加载。
Why?
----
这些约定初看起来会有些小不爽,其实也的确可以通过每次都编译的方式来去掉这些限制。但编译的方式,会给开发调试带来麻烦,代码的实现复杂度也会增加。Sea.js 的核心设计原则是保持简单,遵循 [New Jersey Approach](http://blog.jobbole.com/19062/):
> 简单性:设计必须简单,这既是对实现的要求,也是对接口的要求。实现的简单要比接口的简单更加重要。简单是设计中需要第一重视的因素。
因为简单,所以可靠!
- JavaScript手册
- Array函数
- String函数
- Date函数
- Mach函数
- Regexp函数
- Location函数
- Window 函数
- Other函数
- History函数
- Navigator函数
- Event函数
- Dom函数
- Json函数
- Sea.js手册
- JavaScript学习总结
- 1.基础部分
- 2.对象部分
- 3.BOM和DOM详解
- 4.function函数部分
- 5.原型和原型链详解
- 6.数据类型和JSON格式
- 7.Ajax和Http状态字
- 8.正则表达式
- 9.事件详解
- 前端相关网址
- 前端干货文章
- JavaScript字符串常用的一些方法
- 前端开发知识体系
- JavaScript速成课
- 移动端开发技巧
- 移动端Web页面问题解决方案
- 20个常用的CSS技巧
- 学习JavaScript设计模式
- 前端开发学习总结