Hello,大家好。
在上一篇 webpack練手項目之easySlide(一):初探webpack 中我們一起為大家介紹了webpack的基本用法,使用webpack對前端代碼進行子產品化打包。
但是乍一看webpack隻是将所有資源打包到一個JS檔案中而已,并沒有做到真正的按需加載,這當然不是我們所想要的。
不急,今天的這一章我們就來一起繼續探索webpack的另外一個功能:code split.
1.什麼是code split
英文不好,暫且将其翻譯為代碼分割。也就是我們根據實際業務需求将代碼進行分割,然後在合适的時候在将其加載進入文檔中。
這裡舉一個實際應用場景:上次我們做的圖檔輪播,我們為每張圖檔都添加一個點選事件,點選以後我們彈出一個對話框,裡面介紹一些詳細内容,然後可以點選關閉按鈕進行關閉。
在這個需求中我們發現,對話框這個元件比較特殊,他是在使用者點選圖檔以後才需要加載,如果使用者不點選,那麼他就沒有必要加載出來了。
OK,很好。webpack通過code split方法将頁面必須加載的資源放在bundle.js中,然後對于按需加載的資源通過ajax進行異步加載。
webpack通過 require.ensure 來判斷是否對資源進行按需加載。
下面是官網的簡單用例:
1 require.ensure(["module-a", "module-b"], function(require) {
2 var a = require("module-a");
3 // ...
4 });
2.Demo與Code
同樣的,我已經将上面所說的對話框按需加載實作,大家感興趣的話可以前往檢視:
Demo: http://xiaoyunchen.github.io/easySlide/
Code: https://github.com/xiaoyunchen/easySlide
大家可以打開控制台網絡面闆,檢視資源的加載情況:
頁面加載的時候隻有7個請求,這其中其實并沒有包含我們的對話框元件:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL5gjN2cjM4kjNtEjM5kDN4EjNxEDMyETNxAjMtYDOxYDM28CXyETNxAjMvwlN4EjNwYzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
然後大家可以點選任意一張圖檔,這個時候網絡浏覽器發送了新的網絡請求,然後頁面上也打開了對話框:
這個2.chunk.js也就是webpack為我們打包的對話框元件,包括JS邏輯,HTML模闆以及CSS樣式,稍後我們将為大家作詳細介紹。
(demo上有幾個menu的link,大家先不用管,那是我們下一章将要介紹的内容)
3.元件引用與webpack打包
接下來我們來看看代碼上都發生了哪些變動。
首先是index.html與index.css,并沒有任何修改。(index.html中增加了header與footer,增加了多個圖檔輪播元件,為下一章做的準備。但是都與對話框元件沒有任何關系)
那來看看index.js的修改,具體的源碼大家可以檢視github,這裡隻對後面新增的代碼進行分析:
1 //添加對話框事件
2 var pageDialog=false;
3 $('.pictureShow a').click(function(){
4 var _id=$(this).attr('dialog-for');
5 require.ensure(["../module/dialog.js","../module/dialogConfig.js"], function(require) {
6 var dialogModule=require("../module/dialog.js");
7 var dialogConfig=require("../module/dialogConfig.js");
8 if(!pageDialog){ //判斷對話框元件是否存在,避免重複建立
9 pageDialog=new dialogModule();
10 }
11 pageDialog.openDialogWith(dialogConfig[_id]);
12 });
13 });
首先我們定義了一個對象,然後為頁面上所有的滑動元素增加了一個單擊事件。
第4行:擷取目前事件元素的dialog-for屬性,這是我們在每個滑動元素上新增的屬性,用于指定其對應的對話框id
第5行:使用了webpack的require.ensure異步加載了兩個元件:dialog與dialogConfig,這兩個元件分别是對話框的具體實作邏輯以及對話框内容配置資訊,詳細的代碼我們後面再分析
第6/7行:加載完成後得到兩個元件的引用
第8-10行:判斷pageDialog是否存在,如果不存在我們通過調用dialog元件new一個執行個體,并将指派給pageDialog。
這裡主要是為了避免多次點選時頁面重複建立dialog Html元素,降低頁面性能
第11行:使用pageDialog執行個體,調用openDialogWith方法來打開對話框,同時要從dialogConfig中加載指定的對話框配置内容。
是的,哪怕是在我們沒有檢視dialog元件具體源碼的情況下,整個邏輯還是相對比較清晰的。我們不用理會dialog元件調用了什麼模闆,用了什麼css樣式,内部實作了哪些方法。
定義元件的一大目的就是降低代碼之前的耦合性,作為元件,我隻管定義如何實作一個元件;作為元件調用者,我隻管衣來伸手飯來張口的拿來主義,
不管你内部是如何實作的。
接下來看webpack是如果對上面的代碼進行打包的:
以下是webpack.config.js配置檔案部分内容:
1 module.exports = {
2 entry: {
3 index:"./src/js/page/index.js",
4 delegate:"./src/js/page/jsEvent.js"
5 },
6 output: {
7 path: path.join(__dirname,'dist'),
8 publicPath: "/easySlide/dist/",
9 filename: "[name].js",
10 chunkFilename: "[id].chunk.js"
11 },
12 module: {
13 loaders: [ //加載器
14 {test: /\.css$/, loader: "style!css" },
15 {test: /\.html$/, loader: "html" },
16 {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
17 ]
18 }
19
20 };
改動的地方不多,主要是以下幾點:
第10行:定義了chunk的檔案名命名規則,這裡除了id以外,還可以使用[hash]
第8行:publicPath 這個配置很容易被疏忽。我們的chunk檔案預設是跟bundle放在一塊的,都是在dist目錄下,如果不配置正确的publicPath的話,webpack請求chunk檔案時将會預設請求根目錄
第15行:新增了HTML加載器,主要是給dialog元件加載模闆使用的
codeSplit作為webpack一個比較核心的功能,是以也需要額外的plugins插件配置就能支援。
在項目根目錄下運作 webpack 打包指令以後可以看到dist目錄下就會新增一個 2.chunk.js檔案 (2是chunk的ID值)
值得一提的是,chunk檔案完全由webpack進行管理和使用,我們無需額外的幹預
4.dialog元件的實作
dialog的實作原理比較簡單,我們定義了一個dialog容器,通過css控制其固定在整個頁面之上,然後在生成一個mask陰影層。
具體的實作css樣式大家也可以前往github檢視源碼。
dialog元件的實作方法也是與我們之前做的slideModule比較類似:
第10-11行:加載了dialog元件的HTML模闆,并将其放置在body中。嗯,之所有是要使用html模闆也是為了解耦合,友善對模闆和JS進行單獨維護。這裡webpack在打包的時候
會自動将html模闆壓縮成字元串儲存在chunk檔案中。
第16-44行:定義了dialog元件的幾個接口方法,其中openDialogWith 就是我們之前調用的根據配置内容更新dialog DOM資訊,然後将dialog展示出來。
對于dialogConfig我們也簡單的來看下:
這裡全部都是對話框的資料配置項,單獨維護的好處也是解耦合,以外咱們可以将這些資料配置在資料庫中,通過接同樣的接口格式傳回來就行了。
其實我這裡的元件定義還有一個問題:
我們是将自定義的對象直接作為元件導出,開放給調用者使用。這在操作上其實存在一定的風險性,因為所有接口方法和屬性都是開放的,那就有可能被使用者
給篡改,影響到元件的正常使用。正确的做法是:
在元件内部定義一個私有變量,使用者存放元件所有屬性與方法,然後再将接口方法(一般不建議開放屬性給調用者直接修改)導出給調用者使用。
小結:
通過webpack的codeSplit方法我們可以對代碼進行按需分割以及加載,進而到達頁面性能的最優。