1. 程式人生 > >前端模組化兩兄弟——requireJS和seaJS

前端模組化兩兄弟——requireJS和seaJS

寫在前面

之前沒學過nodeJS,底子不好,對AMD和CMD的實現沒法理解,現在nodeJS也算是步入門檻,再回過身好好研究一下這個“模組化載入器”。http://jafeney.com/2016/01/10/2016-01-10-module/

SeaJS與RequireJS最大的區別

一言以蔽之:執行模組的機制大不一樣

RequireJS 是執行的 AMD 規範, 所有的依賴模組都是先執行,當然 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。這點和常規的nodeJS風格很像,模組編寫前把所有用到的依賴模組率先在頭部注入進來,然後需要呼叫的時候就可以直接拿來用。後端語言大多是這個編碼思路,比較好接受。而SeaJS對模組的態度是懶執行( As lazy as possible),這樣有四個好處:

>
1、防止物件被提前建立(記憶體優化,如載入plist檔案等耗記憶體的操作)
2、防止物件重複建立 (永遠只加載一次)
3、防止物件使用時,還沒被建立
4、可以在懶載入方法裡面,進行初始化操作

舉個簡單的例子:

1234567891011121314151617 // CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... }) // AMD 預設推薦的是 define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ... })

注意,上面也提到了requireJS也支援CMD寫法,同時還支援將 require 作為依賴項傳遞,但 RequireJS 的作者預設是最喜歡上面的寫法,也是官方文件裡預設的模組定義寫法。

此外,AMD 的 API 預設是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 裡,require 分全域性 require 和區域性 require,都叫 require。CMD 裡,沒有全域性 require,而是根據模組系統的完備性,提供 seajs.use 來實現模組系統的載入啟動。CMD 裡,每個 API 都簡單純粹。

豆瓣一個有名的帖子

如下模組分別通過SeaJS/RequireJS來載入:

1234567891011121314 define(function(require, exports, module) { console.log('require module: main'); var mod1 = require('./mod1'); mod1.hello(); var mod2 = require('./mod2'); mod2.hello(); return { hello: function() { console.log('hello main'); } }; });

SeaJS的執行結果

123456 require module: mainrequire module: mod1hello mod1require module: mod2hello mod2hello main

RequireJS執行結果

123456 require module: mod1require module: mod2require module: mainhello mod1hello mod2hello main

小結

SeaJS只會在真正需要使用(依賴)模組時才執行該模組,SeaJS是非同步載入模組的沒錯, 但執行模組的順序也是嚴格按照模組在程式碼中出現(require)的順序, 這樣也許更符合邏輯。

RequireJS會先儘早地執行(依賴)模組, 相當於所有的require都被提前了, 而且模組執行的順序也不一定100%就是先mod1再mod2 。因此你看到執行順序和你預想的完全不一樣。

注意這裡說的是執行(真正執行define中的程式碼)模組, 而非載入(load檔案)模組。模組的載入都是並行的, 沒有區別,區別在於執行模組的時機,或者說是解析。

阻塞問題

SeaJS的懶執行

這裡寫圖片描述

RequireJS的預執行

這裡寫圖片描述

小結

可以很明顯的看出RequireJS的做法是並行載入所有依賴的模組, 並完成解析後, 再開始執行其他程式碼, 因此執行結果只會”停頓”1次, 完成整個過程是會比SeaJS要快.

而SeaJS一樣是並行載入所有依賴的模組, 但不會立即執行模組, 等到真正需要(require)的時候才開始解析, 這裡耗費了時間, 因為這個特例中的模組巨大, 因此造成”停頓”2次的現象, 這就是我所說的SeaJS中的”懶執行”.

總結

jockchou 的看法

我個人感覺requirejs更科學,所有依賴的模組要先執行好。如果A模組依賴B。當執行A中的某個操doSomething()後,再去依賴執行B模組require(‘B’);如果B模組出錯了,doSomething的操作如何回滾?
很多語言中的import, include, useing都是先將匯入的類或者模組執行好。如果被匯入的模組都有問題,有錯誤,執行當前模組有何意義?

總之載入的所有模組,都是當前要使用的,為什麼要動態的去執行?這個問題可以總結為模組的載入執行是靜態還是動態。如果是動態執行的話,那頁面的程式執行過程會受到當前模組執行的影響。而正如樓主所言,動態執行總體時間上是比靜態一次執行要慢的。

樓主說requirejs是坑,是因為你還不太理解AMD“非同步模組”的定義,被依賴的模組必須先於當前模組執行,而沒有依賴關係的模組,可以沒有先後。在樓主的例子中,假設mod1和mod2某天發生了依賴的話,比如在某個版本,mod1依賴了mod2(這是完全有可能的),這個時候seajs的懶執行會不會有問題?而requirejs是不會有問題,也不需要修改當前模組。

在javascript這個天生非同步的語言中,卻把模組懶執行,這讓人很不理解。想像一下factory是個模組工廠吧,而依賴dependencies是工廠的原材料,在工廠進行生產的時候,是先把原材料一次性都在它自己的工廠里加工好,還是把原材料的工廠搬到當前的factory來什麼時候需要,什麼時候加工,哪個整體時間效率更高?顯然是requirejs,requirejs是載入即可用的。為了響應使用者的某個操作,當前工廠正在進行生產,當發現需要某種原材料的時候,突然要停止生產,去啟動原材料加工,這不是讓當前工廠非常焦燥嗎?
暫且不去理會這個吧,等ECMA規範中加入了模組化的定義後,再看誰更合理吧。

玉伯 的觀點

RequireJS 和 SeaJS 都是很不錯的模組載入器,兩者區別如下:
1、兩者定位有差異。RequireJS 想成為瀏覽器端的模組載入器,同時也想成為 Rhino / Node 等環境的模組載入器。SeaJS 則專注於 Web 瀏覽器端,同時通過 Node 擴充套件的方式可以很方便跑在 Node 伺服器端

2、兩者遵循的標準有差異。RequireJS 遵循的是 AMD(非同步模組定義)規範,SeaJS 遵循的是 CMD (通用模組定義)規範。規範的不同,導致了兩者 API 的不同。SeaJS 更簡潔優雅,更貼近 CommonJS Modules/1.1 和 Node Modules 規範。

3、兩者社群理念有差異。RequireJS 在嘗試讓第三方類庫修改自身來支援 RequireJS,目前只有少數社群採納。SeaJS 不強推,而採用自主封裝的方式來“海納百川”,目前已有較成熟的封裝策略。

4、兩者程式碼質量有差異。RequireJS 是沒有明顯的 bug,SeaJS 是明顯沒有 bug。

5、兩者對除錯等的支援有差異。SeaJS 通過外掛,可以實現 Fiddler 中自動對映的功能,還可以實現自動 combo 等功能,非常方便便捷。RequireJS 無這方面的支援。

6、兩者的外掛機制有差異。RequireJS 採取的是在原始碼中預留介面的形式,原始碼中留有為外掛而寫的程式碼。SeaJS 採取的外掛機制則與 Node 的方式一致:開放自身,讓外掛開發者可直接訪問或修改,從而非常靈活,可以實現各種型別的外掛。還有不少細節差異就不多說了。

總之,SeaJS 從 API 到實現,都比 RequireJS 更簡潔優雅。如果說 RequireJS 是 Prototype 類庫的話,則 SeaJS 是 jQuery 類庫。最後,向 RequireJS 致敬!RequireJS 和 SeaJS 是好兄弟,一起努力推廣模組化開發思想,這才是最重要的。

個人之見

AMD速度和效率比CMD快,雖然可能會存在不必要的記憶體開銷,但是對於小專案來說,這些開銷是可以忽略不計的,因為現在的硬體裝置提升的速度很快,而且如果你早已養成良好的編碼習慣,這些差異是肉眼無法捕捉到的,而且AMD思想比較符合常規的程式設計邏輯,更容易讓前端工程師接受,學習成本低。

CMD的好處上面也講了不少了,而且作為國產大頭,我對它的發展還是很看好的,模組的單一職責和延遲載入特性使得程式碼變得優雅、容易維護。對於複雜的前端專案(如webApp),我還是會傾向於seaJS,它的程式設計思想更令我喜愛,奔著早日進軍阿里的信念,很值得花時間去研讀 seaJS的原始碼,去領悟這門框架的精髓,走近開發這門框架的牛人們的世界。

還是那句話,思想沒有好壞之分,只有適不適合你的專案開發。