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個請求,這其中其實並沒有包含我們的對話方塊元件:
然後大家可以點選任意一張圖片,這個時候網路瀏覽器傳送了新的網路請求,然後頁面上也開啟了對話方塊:
這個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方法我們可以對程式碼進行按需分割以及載入,從而到達頁面效能的最優。