webpack 利用Code Splitting 分批打包、按需下載


  之前一直維護的一段廣告js,我都是用webpack作為模塊管理的,由於這種CommonJS的預編譯打包模式,我把所有的模塊都封裝到一個js里面了,請求少了、文件大了。好在大部分的功能模塊都是我手動寫的,引用的三方庫並不多,文件大小還是可控的。但是隨着業務發展的需要,廣告的展示效果越來越豐富,單純的靠“造輪子”,很難高效率、高質量的完成需求。我開始考慮使用一些三方js插件完成一些特定的功能,一來對於很多的特效,它們的方案很成熟、穩定、復用性高。另一個方面,省去了自己開發的成本,可以更快速的完成開發。

  使用三方js插件本就是一件很正常的事,可是我又在糾結什么呢?這不得不和我的自身架構有關,我用webpack把所有的模塊打包在一起,但是三方js插件對應的需求並不是每次打開頁面都要展示,而是依賴着數據請求的返回結果,換句話說我的js並不是經常依賴這個插件,並且插件是一類需求(並非一個)的解決方案,體積不會太小,以我使用的三方插件為例就有77kb而我的代碼本身在壓縮前也才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/";進而影響下面的路徑:

  這樣頁面發出的請求就是在__webpack_require__.p = "http://yoururl.com/" 指定下的靜態服務器的路徑。

  需要注意的是與AMD不同的是AMD中封裝到其他文件的部分模塊可以直接被其他的AMD工程加載,如果是三方的庫更可以作為cdn直接被其他頁面引用。但是webpack打包分離的文件與主文件有着密切的聯系,盡可以被當前主文件使用,每次打包生成兩個文件,建議同步更新上線。

  這樣就利用Code Splitting實現了webpack的分批打包、按需下載

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM