javaScript 模塊化開發 AMD(異步加載 require.js) 和CMD( sea.js) 1

s開發者對js模塊加載的嘗試和創新從來都沒有停止過,尤其是當nodejs的出現後,模塊化加載的必要性更加凸顯。本文不討論如何在nodejs環境來模塊化加載(創造者已經利用commonJS機制解決),只討論在瀏覽器環境下如何來模塊加載的思路,並提出一些我的看法。

瀏覽器環境與nodejs的環境的最大差異是,對於nodejs的環境,大多數情況下被依賴的模塊文件本身就在本地(它們都在服務器上),同步取過來就能用;而對於瀏覽器的環境,被依賴的模塊文件通常還在遠程服務器上,並未加載到本地,也就是說必須是先加載(並解析)後執行的機制。

既然已經有了commonJS,在這之上將異步回調的邏輯加入進去,可是異步先加載什麼呢?於是就有了依賴的概念。一段時間的發展後,有了AMD、和CMD的解決方案,代表作品是requirejs和seajs,有興趣的讀者可以去了解一下,這裏就不展開介紹了。
有必要簡單提一下兩者的主要區別,CMD推崇依賴就近,可以把依賴寫進你的代碼中的任意一行,例:

1
2
3
4
5
6
define(function(require, exports, module) {
  var a = require('./a')
  a.doSomething()
  var b = require('./b')
  b.doSomething()
})

代碼在運行時,首先是不知道依賴的,需要遍歷所有的require關鍵字,找出後面的依賴。具體做法是將function toString後,用正則匹配出require關鍵字後面的依賴。顯然,這是一種犧牲性能來換取更多開發便利的方法。

而AMD是依賴前置的,換句話說,在解析和執行當前模塊之前,模塊作者必須指明當前模塊所依賴的模塊,表現在require函數的調用結構上爲:

1
2
3
4
define(['./a','./b'],function(a,b){
   a.doSomething()
   b.doSomething()
})

代碼在一旦運行到此處,能立即知曉依賴。而無需遍歷整個函數體找到它的依賴,因此性能有所提升,缺點就是開發者必須顯式得指明依賴——這會使得開發工作量變大,比如:當你寫到函數體內部幾百上千行的時候,忽然發現需要增加一個依賴,你不得不回到函數頂端來將這個依賴添加進數組。

細心的讀者可能發現,到目前位置我討論的AMD和CMD的思想的關於依賴的部分,都只討論的“硬依賴”,也就是執行前肯定需要的依賴,但是這不是全部的情況。有的時候情況是這樣的:

1
2
3
4
// 函數體內:
if(status){
  a.doSomething()
}

在這個函數體內,可能依賴a,也可能不依賴a,我把這種可能的依賴成爲“軟依賴”。對於軟依賴當然可以直接當硬依賴處理,但是這樣不經濟,因爲依賴是不一定的,有可能加載了此處的依賴而實際上沒有用上。
對於軟依賴的處理,我推薦依賴前置+回調函數的實現形式。上面的例子簡單表述如下:

1
2
3
4
5
6
// 函數體內:
if(status){
  async(['a'],function(a){
    a.doSomething()
  })
}

至此可以對由commonJS衍生出來的方案做出總結了。在瀏覽器端來設計模塊加載機制,需要考慮依賴的問題。
我們先把依賴分爲兩種,“強依賴” —— 肯定需要 和“弱依賴” —— 可能需要。
對於強依賴,如果要性能優先,則考慮參照依賴前置的思想設計你的模塊加載器,我個人也更推崇這個方案一些;如果考慮開發成本優先,則考慮按照依賴就近的思想設計你的模塊加載器。
對於弱依賴,只需要將弱依賴的部分改寫到回調函數內即可。
如果現在我要實現一個模塊加載器,我會將強依賴前置,弱依賴採用異步回調函數的形式,其它的方法我認爲都只是語法糖而已,僅此就夠了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章