CMD 模塊定義規範
- seajs中,所有的javascript都遵循CMD模塊定義規範。該規範明確定義了模塊的定義格式和模塊依賴的規則說明。
- define.cmd : 一個空對象,可以用來判斷當前頁面是否存在cmd模塊加載器,調用方法如下:
if(typeof define.cmd ==="undefined" || define.cmd){//Seajs存在cmd模塊加載器}
- 與 RequireJS 的 AMD 規範相比,CMD 規範儘量保持簡單,並與 CommonJS 和 Node.js 的 Modules 規範保持了很大的兼容性
- 簡單友好的模塊定義規範:遵循CMD規範。
- 簡單直觀的代碼組織方式:依賴自動加載,配置簡潔清晰。
Chrome 3+ ✔ Firefox 2+ ✔ Safari 3.2+ ✔ Opera 10+ ✔ IE 5.5+ ✔
理論上適用於任何瀏覽器,包括Mobile。
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'
});
可以簡化較長模塊標示的書寫。
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'
}
});
模塊定義:
define(function(require, exports , module){
var $ = require("jquery");
//==>加載的是http://example.com/path/to/base/jquery/jquery/1.10.1/jquery.js
});
如果依賴層次較深,或者跨目錄調用模塊,path可以簡化書寫。
path 可以結合alias一起使用。
seajs.config({
// 路徑配置
paths: {
'gallery': 'https://a.alipayobjects.com/gallery'
},
});
模塊定義:
define(function(require,exports, module){
var json = require("gallery/jsonparser");
//==> 加載的是https://a.alipayobjects.com/gallery/jsonparser.js
});
vars Object
有些模塊需要在運行時才能確定,可以使用vars指定。
vars 配置的是模塊標識中的變量值,在模塊標識中用 {key} 來表示變量。
seajs.config({
// 變量配置
vars: {
'local': 'zh-cn'
},
});
模塊定義
define(function(require, exports, module){
var language_config = require("language/{local}.js");
console.log(language_config);//獲取中文配置文件
});
map Array
路由模塊路徑。
seajs.config({
// 映射配置
map: [
['http://example.com/js/app/', 'http://localhost/js/app/'],
['.js','-debg.js']
],
});
define(function(require, exports, module){
var m_a = require("./a");
//==>加載的是./a-debug.js
});
預加載一些公共模塊或者指定模塊
seajs.config({
// 預加載項
preload: [
Function.prototype.bind ? '' : 'es5-safe',
this.JSON ? '' : 'json'
],
});
preLoad中的配置,需要等到use時才加載
seajs.use("./b",function(){
//在加載b模塊之前,預加載項已經加載完成。
});
preLoad中的配置,無法保證模塊定義中已經加載完成並執行完成。
值爲 true 時,加載器不會刪除動態插入的
script 標籤。插件也可以根據 debug 配置,來決策 log 等信息的輸出。
base String
Sea.js 在解析頂級標識時,會相對 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 可以多次執行,每次執行都會對配置項進行合併操作。
config 會自動合併不存在的項,對存在的項則進行覆蓋。
建議seajs.config所在的js文件
獨立成一個文件時,一般通過 script 標籤在頁面中同步引入。
定義函數:
函數:define(factory)
其中,define方法是全局方法,作用域是window;factory可以是字符串,對象或者函數。
Factory類型:
- 字符串或者對象。
define({"username":"fol","age":"23"});
//factory是對象類型
define("I love zhuzhou");
//factory是字符串類型
如果模塊的factory類型是字符串或者對象,那麼該模塊對外提供的接口(輸出)就是字符串或者對象本身。
- 函數
define(function(require, exports, module){
//todo
});
如果factory的類型是函數,那麼給函數就是該模塊的構造函數,執行該函數可以得到模塊對外提供接口。
factory默認傳入的參數是require, exports, module。
這種模塊定義方式未指定模塊id,模塊依賴列表。
定義格式2:
define(id?
, deps?, factory?)
模塊定義也可以接受兩個以上參數,其中id定義模塊的id,deps聲明模塊依賴的其他模塊列表(Array)。
define("user",
["jquery", "ajax", "upload", "json"], function(require, exports, module){
// todo
});
注意:id 和 deps參數是可選參數,可以通過構建工具自動生成。
模塊引用(require
Function)
模塊加載函數:
函數:require( id? )
其中,require對象是factory的第一個參數,同樣,require函數也是全局函數,作用域是window;
id是模塊標識,一般在模塊定義函數中被調用,用來獲取其他模塊提供的接口。
define(function( require, exports,
module ) {
var $ = require("jquery");
//引入jquery模塊
$(document).ready(function(){
$("#bt").on("click",
function(){
console.log("the
bt is clicked");
});
});
});
異步加載:
函數:require.async( id?, callback? )
其中,id是模塊的標識,callback是模塊異步加載完成後的回調函數。
define(function(require, exports,
module){
//異步加載單個模塊
require.async("plugins/bootstrap",
function(b){
b.doSomething();
});
//異步加載多個模塊
require.async(["./utils/datepicker","./utils/colorpicker"],
function(datepicker, colorpicker){
datepicker.init();
colorpicker.init();
});
});
注意:require()是同步執行的,require.async()是異步回調執行,require.async用來執行可以延遲加載執行的模塊。
函數:require.resolve(id?)
其中,id 是模塊的唯一標識,只用來解析模塊的絕對路徑。
define(function(require, exports,
module){
val jqueryPath = require.resolve("jquery");
//解析jquery的絕對路徑
//http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.js
});
seajs 三個對象
exports Object
exports 是一個對象,負責對外提供模塊接口。
define(function(require, exports,
module){
function trim() {
return this.replace(/(^\s*)|(\s*$)/g, "")
}
exports.trim = trim; //把內部函數暴露給其他模塊
exports.config = {"username":"foo", "age":"23"};//把內部對象暴露給其他模塊
});
除了使用exports 對象對外提供接口外,還可以使用return直接對外提供接口
define(function(require,
exports, module){
function trim() {
return this.replace(/(^\s*)|(\s*$)/g, "")
}
return {
"trim":trim,
"config":{"username":"foo", "age":23}
}
});
如果模塊中return語句是唯一的代碼,那麼可以使用define({})簡化模塊定義:
define({
"trim":function(){
return this.replace(/(^\s*)|(\s*$)/g, "")
},
"config":{"username":"foo","age":23}
}
);
上面的格式可以適合定義JSONP模塊。
注意:不能對exports重新賦值,這樣雖然不會影響到module.exports 。但是無法對外提供接口,可以賦值module.exports達到對外提供接口的目的。
define(function(require, exports , module){
module.exports = {
trim:function(){return this.replace(/(^\s*)|(\s*$)/g, "");},
config:{"username":"foo", "age":23}
}
});
module Object
module是一個對象,其中存儲着於當前模塊相關的一些方法和屬性。
module.id:返回模塊的標識
module.uri:返回模塊的絕對路徑。
define(function(require, exports, module){
var module_uri = module.uri;
//==>
http://example.com/path/to/this/file.js
});
一般情況下,如果沒有定義模塊id, 那麼module.id ==
module.uri,兩者完全相同。
module.dependencies Array:返回當前模塊依賴的模塊列表。
module.exports:返回當前模塊對外提供的接口對象。
注意:傳遞給factory方法的參數中exports 只是module.exports 的一個引用,只能通過exports來對外提供接口,但是module.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 不能異步執行
setTimeout(function(){
module.exports = {"username":"foo", "age":23}; //錯誤,無法對外提供接口
}, 1000);
});
seajs.use Function
函數:seajs.use(ids?,
callback?)
作用:用在頁面上加載其他模塊。
//加載一個模塊
seajs.use("./a");
//加載一個模塊並回調
seajs.use("./a", function(a){
a.doSomething();
});
//加載多個模塊並回調
seajs.use(["jquery","./a"],
function($, a){
$("#bt").click(function(){
//doSomething
});
a.doSomething();
});
注意:seajs.use方法和document.ready()方法沒有必然關係,如果某些操作需要在document ready後才能執行操作,需要藉助jquery等依賴模塊。
函數:seajs.cache();
seajs.resolve Function
seajs.data Function
可以用來查看當前頁面加載的依賴模塊列表。
函數:seajs.resolve(id?);
可以用來獲取依賴模塊的絕對路徑。
函數:seajs.data();
模塊標識與路徑關係
可以用來seajs的所有配置以及一些內部變量,可以用在插件開發中。
模塊標識:
模塊標識主要以小駝峯,. 或者 .. 爲主。
//在http://example.com/my/js/user.js中
define(function(require, exports, module){
var path = require.resolve("./json");
//==>路徑爲http://example.com/my/js/json.js
var path2 = require.resolve("../json");
//==> 路徑爲http://example.com/my/json.js
});
注意:以小駝峯開頭的模塊標識是頂級標識,以base爲根目錄加載模塊文件;以.和..開頭的模塊標識是相對標識,以當前模塊文件所在的位置爲基礎根目錄加載。
//假設base 是http://examle.com/my/js
define(function(require, exports, module){
var path = require.resolve("json");
//==>路徑爲http://example.com/my/js/json.js
});
注意:頁面中基於當前頁面爲根目錄加載模塊文件。
構建工具那些事
構建過程描述:
- 提取操作
提取模塊的標識id以及模塊的其他依賴dependencies。
//a.jsdefine(function(require, exports, module){var b = require("./b");});
經過提取操作,a.js文件會變成臨時文件:
define("xxx/1.0.0/a",["./b"],function(require, exports, module){var b = require("./b");});
- 壓縮操作
經過上面的提取操作後,構建工具就可以調用任何 JS 壓縮工具來進行壓縮了,require 參數也可以被壓縮成任意字符。相比於其他的壓縮工具,CMD模塊的構建過程增加了id和dependencies的提取過程。
爲什麼要提取模塊標識Id:
我們在模塊定義過程中,可能會合並兩個模塊定義文件,使得模塊管理更加方便和易用。
//a.js
define(function(require, exports, module){
//todoSomething
});
//b.js
define(function(require, exports, module){
//todoSomething
});
如果我們希望合併以上兩個文件,那麼會出現模塊定義不清楚,無法確認加載哪個模塊的問題,所以在模塊構建過程中需要提取模塊標識id。
爲了保證壓縮文件隨意壓縮代碼,構建工具在提取id的同時,也會提取dependencies數組。這樣seajs不會再通過factory.toString()藉助於正則匹配來獲取依賴,直接可以通過factory函數的第二個參數拿到依賴數組。
注意:一旦factory的第二個參數定義了依賴數組,那麼seajs將不會使用正則匹配的方式去分析並獲取依賴,而是直接使用factory第二個參數提供的依賴數組作爲所有的依賴。