標籤:
之前一直維護的一段廣告js,我都是用webpack作為模組管理的,由於這種CommonJS的先行編譯打包模式,我把所有的模組都封裝到一個js裡面了,請求少了、檔案大了。好在大部分的功能模組都是我手動寫的,引用的三方庫並不多,檔案大小還是可控的。但是隨著業務發展的需要,廣告的展示效果越來越豐富,單純的靠“造輪子”,很難高效率、高品質的完成需求。我開始考慮使用一些三方js外掛程式完成一些特定的功能,一來對於很多的特效,它們的方案很成熟、穩定、複用性高。另一個方面,省去了自己開發的成本,可以更快速的完成開發。
使用三方js外掛程式本就是一件很正常的事,可是我又在糾結什麼呢?這不得不和我的自身架構有關,我用webpack把所有的模組打包在一起,但是三方js外掛程式對應的需求並不是每次開啟頁面都要展示,而是依賴著資料請求的返回結果,換句話說我的js並不是經常依賴這個外掛程式,並且外掛程式是一類需求(並非一個)的解決方案,體積不會太小,以我使用的三方外掛程式為例就有77kb而My Code本身在壓縮前也才127kb。將外掛程式混入必然造成檔案大小的增大、載入時間變長、廣告渲染延遲等一些列問題。何況隨著業務的發展,我將來要引用的三方外掛程式不止一個。遇事我想到了曾經偶爾看到webpack中的解決方案——Code Splitting。
簡單來說就是按需載入(下載),如果是requireJS對應的AMD的方案中這本是在正常不過了。但是在webpack中All in one的思想就會顯得很怪,但webpack並不死板(就像某著名AMD和CMD模組管理器中都有對方陣營的實現方案)。我查閱了不少的文檔和論壇,終於找到了webpack中對於按需下載的支援方案(此處想吐槽webpack官方文檔),好多的論壇文章都提到了使用require.ensure 但是卻寫得很簡略,直接使用發現行不通、感覺少了點啥,比如下面的寫法:
1 require.ensure([‘需要動態載入的module‘], function(require) {2 require(‘需要動態載入的module‘);3 });4 //webpack會自動產生動態載入代碼,結果和ensure產生的是一樣的
我這裡就詳細整理一下,這種用法到底怎麼用。對應的配置、發布流程是什麼樣的。
webpack的整體風格是All in one 就是將從入口檔案開始的所有引用到的模組都打包到一個js檔案(包括css 、圖片,這裡只說js)。在打包的過程中會有一系列的名稱替換,細心的朋友會發現我們的模組被命名為更加簡單,節省空間的數字了。不同於requireJS可以直接require線上檔案的方式。webpack推薦將所有可能用到的檔案(模組)都打包。但是這樣做僅適合使用率較高的小體積模組。對於體積大有不常用的模組,反而顯得不適合。如果你嘗試過在webpack工程中直接require線上url。啟動並執行時候會拋出一個異常——不能找到模組,那是因為對於打包後的js中預設是在這個包內尋找模組,或者說webpack工程中的require函數預設是在包(檔案)內找,壓根兒就沒打算髮起http請求。所以就不要想直接require線上的url了。
拿到webpack工程就只能打包在一起,搞出一個超大的檔案,把頁面卡住?webpack官方給出瞭解決方案——Code Splitting。這種方式並不代表webpack有意投靠AMD,而是另闢蹊徑彌補了自身的短板。簡單來說,還是All in one 還是打包成一個包,還是在包內部找模組,只是這一個包不一定只有一個檔案。我們直譯Code Splitting為“代碼拆分”,也就是說我們可以理解為將all in one的包在拆成多個包,當需要引用的時候再引用下載、載入,只是這種載入是通過webpack內部機制發起http請求實現的。我們將摸個不常用的大體積模組從包中分離出來,當包內的語句引用到了這個模組後,webpack會判斷這個模組是被分離出去的,應當發起http請求拉取。不是不能拉向上模組,只是只拉和自己有淵源的模組。
那麼webpack怎麼知道這個模組被分離了?這就降到了上面提到的 require.ensure ,通過不同的API就相當於一種標記,在打包時分出去、引用時發請求動態載入。其實大家最關心的是怎麼用?舉個例子:我要引用A、B、C、D 四個外掛程式,A、C可能往往一起用,B、D可能往往一起用。我就需要在webpack打包後產生3個檔案:包含主檔案的js(大量常用模組)、包含A和C的分離包、包含B和D的分離包。
1 /*別的事XXXX*/ 2 if(bIsAC === true ){ 3 require.ensure(["A","C"], function(require){ 4 var A = require(‘A‘); 5 var C = require("C"); 6 var ac = A+C; 7 /*一大堆商務邏輯*/ 8 }); 9 10 }else{11 require.ensure(["B","D"], function(require){12 var B = require(‘B‘);13 var D = require("D");14 var bd = B+D;15 /*一大堆商務邏輯*/16 }); 17 }18 /*別的事XXXX*/
這時候編譯webpack,會產生3個檔案。如果你在webpack.config.js檔案中定義輸出是bundle.js,同時還會產生兩個檔案1.bundle.js和2.bundle.js,把他倆放在我們實驗的頁面的同一級目錄下載會發現頁面先載入bundle.js。然後根據條件載入1.bundle.js或2.bundle.js。為了更直觀的看到現象我去掉了if判斷載入3個檔案()。
問題來了:兩個分離的模組檔案一定要放在頁面同一級嗎?當然是可以自訂的。有一個細節,我們並沒有設定過引用1.bundle.js和2.bundle.js的線上url的語句,看來它必然是有預設設定的。如果對webpack有瞭解的朋友一定會想到webpack.config.js檔案。就是兩個分離檔案的根路徑:
它會直接影響編譯後的 __webpack_require__.p = "http://yoururl.com/";進而影響下面的路徑:
這樣就利用Code Splitting實現了webpack的分批打包、按需下載
webpack 分批打包、按需下載