[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設計模式
- 前端開發學習總結