1. 程式人生 > 實用技巧 >前端框架 seajs 使用總結

前端框架 seajs 使用總結

CMD 模組定義規範

  • seajs中,所有的javascript都遵循CMD模組定義規範。該規範明確定義了模組的定義格式和模組依賴的規則說明。
  • define.cmd: 一個空物件,可以用來判斷當前頁面是否存在cmd模組載入器,呼叫方法如下:
if(typeof define.cmd ==="undefined" || define.cmd){ //Seajs存在cmd模組載入器 }
  • 與RequireJS的AMD規範相比,CMD規範儘量保持簡單,並與CommonJS和Node.js的Modules規範保持了很大的相容性

seajs的特點


  • 簡單友好的模組定義規範:遵循CMD規範。
  • 簡單直觀的程式碼組織方式:依賴自動載入,配置簡潔清晰。

seajs的相容性


Chrome 3+ ✔ Firefox 2+ ✔ Safari 3.2+ ✔ Opera 10+ ✔ IE 5.5+ ✔

理論上適用於任何瀏覽器,包括Mobile。




seajs的配置

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

可以簡化較長模組標示的書寫。 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 Object

如果依賴層次較深,或者跨目錄呼叫模組,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 });

preLoad Array

預載入一些公共模組或者指定模組 seajs.config({ //預載入項 preload:[ Function.prototype.bind?'':'es5-safe', this.JSON?'':'json' ], }); preLoad中的配置,需要等到use時才載入 seajs.use("./b",function(){ //在載入b模組之前,預載入項已經載入完成。 }); preLoad中的配置,無法保證模組定義中已經載入完成並執行完成。

debug String

值為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 Function) 定義函式:
函式:define(factory) 其中,define方法是全域性方法,作用域是window;factory可以是字串,物件或者函式。 Factory型別:
  • 字串或者物件。
define({"username":"fol","age":"23"}); //factory是物件型別 define("I love zhuzhou"); //factory是字串型別 如果模組的factory型別是字串或者物件,那麼該模組對外提供的介面(輸出)就是字串或者物件本身。
  • 函式
定義格式1:
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){ functiontrim(){ returnthis.replace(/(^\s*)|(\s*$)/g,"") } exports.trim = trim; //把內部函式暴露給其他模組 exports.config = {"username":"foo", "age":"23"};//把內部物件暴露給其他模組 }); 除了使用exports 物件對外提供介面外,還可以使用return直接對外提供介面 define(function(require, exports, module){ functiontrim(){ returnthis.replace(/(^\s*)|(\s*$)/g,"") } return { "trim":trim, "config":{"username":"foo", "age":23} } }); 如果模組中return語句是唯一的程式碼,那麼可以使用define({})簡化模組定義: define({ "trim":function(){ returnthis.replace(/(^\s*)|(\s*$)/g,"") }, "config":{"username":"foo","age":23} } ); 上面的格式可以適合定義JSONP模組。 注意:不能對exports重新賦值,這樣雖然不會影響到module.exports 。但是無法對外提供介面,可以賦值module.exports達到對外提供介面的目的。 define(function(require, exports , module){ module.exports = { trim:function(){returnthis.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 Function

函式:seajs.cache();
可以用來檢視當前頁面載入的依賴模組列表。 seajs.resolve Function 函式:seajs.resolve(id?);
可以用來獲取依賴模組的絕對路徑。

seajs.data Function

函式: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.js define(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,那麼在 IE6-9 下也有可能會出現問題。

為什麼要提取模組依賴dependencies: 為了保證壓縮檔案隨意壓縮程式碼,構建工具在提取id的同時,也會提取dependencies陣列。這樣seajs不會再通過factory.toString()藉助於正則匹配來獲取依賴,直接可以通過factory函式的第二個引數拿到依賴陣列。 注意:一旦factory的第二個引數定義了依賴陣列,那麼seajs將不會使用正則匹配的方式去分析並獲取依賴,而是直接使用factory第二個引數提供的依賴陣列作為所有的依賴。