JavaScript模組化開發庫之SeaJS

來源:互聯網
上載者:User

JavaScript模組化開發庫之SeaJS
SeaJS由國內的牛人lifesinger開發。目前版本是1.1.1,源碼不到1500行,壓縮後才4k,品質極高。
這篇會講述SeaJS的一些基本用法,不會面面俱到,但會就個人的理解講述官方文檔沒有提到的一些細節。
 
一、SeaJS的全域介面
 
SeaJS向全域公開了兩個標識符: seajs 和 define。
 
如果你的項目中已經用了標識符seajs,又不想改。這時SeaJS可以讓出全域的seajs。如
var boot = seajs.noConflict();
這時boot就相當於先前的seajs。
 
如果你的項目中連標識符define也用到了,也不想改。SeaJS是很寬容的,它的define也可以讓出。如
var boot = seajs.noConflict(true);
較上面僅多傳了一個true。這時全域的define也沒了。這時需要用boot.define來代替之前的define。
 
用過jQuery的同學應該很熟悉$.noConflict方法,SeaJS的noConflict與之類似。
 
二、SeaJS的模組寫法
 
SeaJS預設使用全域的define函數寫模組(可把define當成文法關鍵字),define定義了三個形參id, deps, factory。
 
define(id?, deps?, factory);
 
這個define很容易讓你想起AMD的唯一API:define函數。 或者說讓人費解,導致搞不懂SeaJS和 RequireJS define的區別。
 
它們都有個全域的define,形參都是三個,且對應的形參名也一樣,會誤認為SeaJS也是AMD的實現。
 
事實上SeaJS和RequireJS的define前兩個參數的確一樣。
 
id都為字串,都遵循 Module Identifiers。deps都是指相依模組,類型都為數組。區別僅在於第三個參數factory,雖然類型也都是函數,但factory的參數意義卻不同。
 
RequireJS中factory的參數有兩種情況
a、和deps(數組)元素一一對應。即deps有幾個,factory的實參就有幾個。
 define(['a', 'b'], function(a, b){
    // todo
});
  
b、固定為require,exports, module(modules/wrappings格式)。
 define(function(require, exports, module){
    // todo
});
  
這種方式是RequireJS後期向 Modules/Wrappings 的妥協,即相容了它。而SeaJS的define僅支援RequireJS的第二種寫法:Modules/Wrappings。
注意:SeaJS遵循的是 Modules/Wrappings 和 Modules/1.1.1。這兩個規範中都沒有提到define關鍵字,Modules/Wrapping中要求定義模組使用module.declare而非define。而恰恰只有AMD規範中有define的定義。即雖然SeaJS不是AMD的實現,但它卻採用了讓人極容易誤解的標識符define。
 
 
說了這麼多,還沒開始寫一個模組。下面我們從最簡單的開始
1、簡單模組
 define({
    addEvent: function(el, type, fn){},
    removeEvent: function(el, type, fn){},
    fireEvent: function(el, type){}
});
  
這樣就寫了一個事件模組,這和寫一個單例沒有區別。更多的時候用該方式定義純資料模組。它類似於
 var E = {
    addEvent: function(el, type, fn){},
    removeEvent: function(el, type, fn){},
    fireEvent: function(el, type){}
};
  
2、簡單的封裝模組
 define(function() {
    // 一些內部輔助函數
    // ...
    function addEvent() {
        // ..
    }
    function removeEvent() {
        // ..
    }
    function fireEvent() {
        // ..
    }
    return {
        addEvent: addEvent,
        removeEvent: removeEvent,
        fireEvent: fireEvent
    };
});
  
您懂的,在這個匿名函數中可以做很多事情。最後只需公開必要的介面。它類似於
 var E = function() {
    // 一些內部輔助函數
    // ...
    function addEvent() {
        // ..
    }
    function removeEvent() {
        // ..
    }
    function fireEvent() {
        // ..
    }
    return {
        addEvent: addEvent,
        removeEvent: removeEvent,
        fireEvent: fireEvent
    };
}();
  
3、NodeJS風格的封裝模組
 
上面兩種寫法看不到一絲NodeJS風格(Modules/1.1.1),改寫下與“方式2”等價的。
 define(function(require, exports) {
    // 一些內部輔助函數
    // ...
    function addEvent() {
        // ..
    }
    function removeEvent() {
        // ..
    }
    function fireEvent() {
        // ..
    }
    // 使用exports匯出模組介面,而非返回一個對象
    exports.addEvent = addEvent;
    exports.addEvent = removeEvent;
    exports.addEvent = fireEvent;
});
  
可以看到與“方式2”區別在於:
1:匿名函數有兩個參數require、exports。
2:匯出介面不是return一個對象而是使用exports。
而exports不正是NodeJS的風格嗎? 細心的同學可能發現這個樣本中require參數沒有用到,這正是下面要講的。
 
4、有依賴的模組
 
SeaJS中“依賴”都需要使用require函數去擷取,雖然SeaJS的define的第二個參數deps也有“依賴”的意思,但它是提供打包工具(SPM)用的。此外,SeaJS的require是作為參數傳入匿名函數內的,RequireJS的require則是全域變數。 www.2cto.com
 
上面定義的是一個沒有依賴的模組,以下是有依賴的模組。
 define(function(require, exports) {
    var cache = require('cache');
     
    // ...
     
    exports.bind = bind;
    exports.unbind = unbind;
    exports.trigger = trigger;
});
  
該事件模組依賴於cache模組,函數有兩個形參require和exports。拋開外層的匿名函數及define,它就是標準的NodeJS格式:使用require函數取相依模組,使用exports匯出現有模組介面。
實際上在SeaJS中具有依賴的模組必須按“方式4”寫,即必須是封裝模組,且匿名函數的第一個參數必須是標識符 “require”。即可以把require當初文法關鍵字來使用,雖然它不是全域的。
 
下面我們看看匿名函數的參數require和exports的一些有趣現象
a、如果寫的不是require,改成req會是什麼結果。
 define(function(req, exports) {
    var cache = req('cache');
     
    // ...
     
    exports.bind = bind;
    exports.unbind = unbind;
    exports.trigger = trigger;
});

Firebug網路請求如下
 
會看到依賴的“cache”沒有被載入,當然JS肯定會報錯了。

b、只把匿名函數的形參改成req,函數內部仍然使用require。
 define(function(req, exports) {
    var cache = require('cache');
     
    // ...
     
    exports.bind = bind;
    exports.unbind = unbind;
    exports.trigger = trigger;
});
  
看網路請求
 
這次“cache”模組竟然請求下來了。
 
仔細看上面的匿名函數代碼中require沒聲明,且形參req而非require。那
?
1 var cache = require('cache');
中的require從何而來?
 
看SeaJS源碼可知,它的define函數中會取該匿名函數的toString,使用正則匹配解析出其中的“cache”(私人的parseDependencies函數)。
 
我們也看到,雖然cache請求下來了,卻仍然報錯,因為在執行階段require是未定義的。因此寫相依模組時匿名函數的第一個參數必須為require且不能更改。
 
正因為使用factory.toString和正則解析依賴,因此require的參數不能是運算式,如
// require的參數不能是運算式運算
require("ui-" + "dialog");

也不能使用require的別名,如
// 不能將require賦值給另外一個變數
var req = require;
req("ui-dialog");
  
c、修改exports為expo
 define(function(require, expo) {
    var cache = require('cache');
     
    // ...
     
    expo.bind = bind;
    expo.unbind = unbind;
    expo.trigger = trigger;
});
  
運行是沒有問題的。即第二個參數“exports”是可以自訂的。顯然SeaJS不贊成改“exports”為其它,這樣明顯破壞了NodeJS風格(Modules/1.1.1)的模組規範---它們正是使用“exports”匯出模組介面。但這點在SeaJS中卻無法被強制執行,只能是人為約定。
 
5、混合寫法的模組
 
上面已經介紹了各種情形中的模組寫法。為了與NodeJS風格保持一致:使用require擷取“依賴”,使用exports匯出“介面”。SeaJS在擷取依賴這一塊做了限制,即必須使用require。但匯出則不一定非得使用exports,即exports可以改為其它。甚至還可以直接使用 “傳回值”。
 define(function(require) {
    var cache = require('cache');
     
    // ...
     
    // 使用傳回值匯出介面
    return {
        bind: function() {},
        unbind: function() {},
        fire: function() {}
    };
});
  
我們知道在NodeJS中模組只能是一個對象。即總是往exports上掛方法。SeaJS中如果使用exports匯出介面,那麼也一樣,模組也只能是JS對象。如果使用“傳回值”匯出介面的話,那麼模組可以是任意的JS類型。如下將返回一個函數類型的模組。
define(function(require) {
    var cache = require('cache');
     
    function ad() {
        //...
    }
     
    // 函數類型的模組
    return ad;
});
  
三、SeaJS的載入方式
 
雖然它提供各種方式(同步非同步)載入,最簡單的莫過於直接在頁面中寫script標籤。引入SeaJS後,入門多數時候就是seajs.use方法。
seajs.use有兩個參數,第一個參數可以為字串(模組名)或數組(多個模組)。第二個參數是回呼函數。模組載入後的回調。回呼函數的參數與第一個參數一一對應。
seajs.use('dom', function(dom) {
    // todo with dom
});
  
如下將在回呼函數中使用dom模組。當然它也提供了捷徑data-main(同RequireJS)。

 

摘自  Snandy 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.