如今模塊化的 Javascript 的開發(fā)越來越火熱,無論是模塊加載器還是優(yōu)秀的 Javascript 模塊,都是層出不窮。既然這么火,肯定是有存在的理由,肯定是解決了某些實(shí)際問題。很多沒接觸過模塊化 Javascript 開發(fā)者不禁要問,我真的需要模塊化嗎,模塊化相比于傳統(tǒng)的模式有什么優(yōu)勢?
Javascript 本身是沒有模塊化支持的,很多語言多有,就連CSS都有這樣的加載方式。
@import "fed.css";
雖然因?yàn)樾阅軉栴}不推薦CSS這樣來進(jìn)行加載,但這是一種模塊化的思想,這種思想對于 Javascript 來說很有用。幸好Javascript是一門靈活的語言,可以通過下面這段代碼來進(jìn)行動態(tài)加載 Javascript 文件。
var script = document.createElement( 'script' ),
head = document.head;script.src = 'http://example.com/test.js';
script.async = 'async';
head.insertBefore( script, head.firstChild );
傳統(tǒng)的加載方式必須在頁面中放置一個 script 標(biāo)簽來進(jìn)行加載。
<script type="text/javascript" src="http://example.com/test.js"></script>
更好的分離
那如果要加載多個就得放置多個 script 標(biāo)簽,如果是加載模塊的話,拿 easy.js 來說,頁面中始終只要引用 easy.js 即可,這樣對于 HTML 和 Javascript 分離很有好處,在某些場景下這個分離度很重要。
就拿我司的情況來說吧。后端的 view 層并不是由我們前端來開發(fā)的,項(xiàng)目上線的時候,前端只能更新 CSS 和 JS 文件,HTML 文件動不了。如果前端要在該頁面新增一個 Javascript 文件的引用是很麻煩的,因?yàn)楹蠖顺绦虻母露家獓?yán)格按照流程來并有固定的更新時間,如果是用模塊加載的方式不用再理會后端的更新。當(dāng)然,你也許會說,如果原頁面中已經(jīng)有 Javascript 文件,我直接在原文件中加代碼不就行了,那么接下來說說直接在原文件中新增代碼會碰到什么樣的問題。
更好的代碼組織方式
如果單個文件越來越大,維護(hù)起來出問題的幾率也會越來越大,一個人開發(fā)還好,如果是多人開發(fā),不同的代碼風(fēng)格,超多的業(yè)務(wù)邏輯混雜在一起,不要說維護(hù)了,光想想都蛋痛。模塊式的開發(fā),一個文件就是一個模塊,控制了文件的粒度,每個模塊可以專注于一個功能。正所謂一個蘿卜一個坑,多人開發(fā)時,各自管好自己坑里的蘿卜就行了,這也正是 OOP 的思想。
按需加載
還是圍繞單個文件來說事,當(dāng)文件大到一定的程度,性能問題也隨之而來了。合并文件是能減少請求,這是會帶來性能的提升,但是當(dāng)文件大到一定的體積時,此時的下載時間可能并不會比多個小文件的下載時間更短。此時就需要權(quán)衡請求數(shù)和文件體積的關(guān)系了。
單文件還有一個問題,那就是緩存是否能充分的利用好。如果一個大體積的文件內(nèi)包含了超多的業(yè)務(wù)邏輯和復(fù)雜的功能,而這個文件同時又被很多頁面引用到。比如在某頁面,實(shí)際只用到了該文件一個很少的功能,那么其他的代碼對于該頁面來說就是多余的,浪費(fèi)了加載流量。你或許會說,這個文件雖然大,但是它第一次加載的時候就被緩存過了,盡管在某頁面只用到了極少部分的功能,但只要緩存過,加載還是挺快的。是的,如果情況有這么理想肯定是好事。但是,往往理想和現(xiàn)實(shí)都會有差距,如果產(chǎn)品的迭代更新太快,業(yè)務(wù)需求一天一個樣,那么該文件就會三天兩頭的更新,更新可能是一個很小的功能,但是這樣的更新對于刷新緩存的代價(jià)可是很大的。如果確實(shí)有這種情況,就說明這種設(shè)計(jì)確實(shí)是有問題。如果能合理的對文件進(jìn)行模塊化的管理,那么可以盡量減少不必要的加載,盡量減少刷新大文件的緩存帶來的損失,這也需要權(quán)衡好,比如將很少更新的合并成一個文件,常更新的獨(dú)立成模塊。
避免命名沖突
Javascript 本身是沒有命名空間的,為了避免命名沖突,經(jīng)常會使用對象和閉包的辦法來避免。用對象僅僅是降低了沖突的概率而已,拿經(jīng)常使用 jQuery 的開發(fā)來說,無論是往 $ 上擴(kuò)展還是在 $.fn 上擴(kuò)展,人多了難免會起沖突。或者用自定義的對象,搞個好幾層,不光是寫起來難記,這樣的調(diào)用也會在性能上打折扣的。模塊化就很好的解決了這個問題,在該模塊內(nèi)的任何形式的命名都不會再和其他模塊有沖突,你想起啥名都行了。當(dāng)然,你硬要在模塊內(nèi)部給 window 上掛一堆東西,我也沒辦法。
既然每個模塊都是封閉式的,那么模塊之間如何通信呢?接口的設(shè)計(jì)那是必須的。很簡單,對于像 seajs 這種 CMD 規(guī)范的,需要使用自定義的關(guān)鍵字來向外部暴露一個接口。
define(function( require, exports ){
var hello = 'hello world';
// 向外部暴露該模塊的接口
exports.hello = hello;
});
easy.js 遵循 AMD 規(guī)范,暴露接口更簡單,直接用 return 關(guān)鍵詞即可。
define( 'hello', function(){
var hello = 'hello world';
// 向外部暴露該模塊的接口
return hello;
});
更好的依賴處理
傳統(tǒng)的開發(fā)模式,如果 B 文件要依賴 A 文件,那么必須在 B 文件前面用 script 的形式先加載好 A 文件。如果有一天,B 文件不再需要依賴 A 文件,或者要增加依賴文件 C,那么又回到了我說的第一個問題上。如果這個 B 文件被 N 個頁面在調(diào)用,而且頁面還跨業(yè)務(wù)站點(diǎn),那改起來簡直就是噩夢啊。如果是用模塊化,只需要在模塊內(nèi)部聲明好依賴就行了,增加刪除都直接修改模塊即可。調(diào)用的時候也不用管該模塊依賴了哪些其他模塊,放心的用就是了。
好了,講了這么多,如果你還是覺得無動于衷,要么是你接觸到的項(xiàng)目較小,還沒復(fù)雜到這個程度,要么就是我的表達(dá)能力有問題。如果你覺得還有什么需要補(bǔ)充的,歡迎在下面留言。
文章來源:雨夜帶刀‘s Blog,轉(zhuǎn)載請注明出處。