webpack多頁應(yīng)用架構(gòu)系列(十一):預(yù)打包Dll,實現(xiàn)webpack音速編譯

本文首發(fā)于Array_Huang的技術(shù)博客——實用至上,非經(jīng)作者同意,請勿轉(zhuǎn)載。
原文地址:https://segmentfault.com/a/1190000007104372
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang

前言

書承上文《webpack多頁應(yīng)用架構(gòu)系列(十):如何打造一個自定義的bootstrap》。

上文說到我們利用webpack來打包一個可配置的bootstrap,但文末留下一個問題:由于bootstrap十分龐大,因此每次編譯都要耗費大部分的時間在打包bootstrap這一塊,而換來的僅僅是配置的便利,十分不劃算。

我也并非是故意賣關(guān)子,這的確是我自己開發(fā)中碰到的問題,而在撰寫完該文后,我立即著手探索解決之道。終于,發(fā)現(xiàn)了webpack這一大殺器:DllPlugin&DllReferencePlugin,打包時間過長的問題得到完美解決。

解決方案的機制和原理

DllPlugin&DllReferencePlugin這一方案,實際上也是屬于代碼分割的范疇,但與CommonsChunkPlugin不一樣的是,它不僅僅是把公用代碼提取出來放到一個獨立的文件供不同的頁面來使用,它更重要的一點是:把公用代碼和它的使用者(業(yè)務(wù)代碼)從編譯這一步就分離出來,換句話說,我們可以分別來編譯公用代碼和業(yè)務(wù)代碼了。這有什么好處呢?很簡單,業(yè)務(wù)代碼常改,而公用代碼不常改,那么,我們在日常修改業(yè)務(wù)代碼的過程中,就可以省出編譯公用代碼那一部分所耗費的時間了(是不是馬上就聯(lián)想到坑爹的bootstrap了呢)。

整個過程大概是這樣的:

  1. 利用DllPlugin把公用代碼打包成一個“Dll文件”(其實本質(zhì)上還是js,只是套用概念而已);除了Dll文件外,DllPlugin還會生成一個manifest.json文件作為公用代碼的索引供DllReferencePlugin使用。
  2. 在業(yè)務(wù)代碼的webpack配置文件中配置好DllReferencePlugin并進行編譯,達到利用DllReferencePlugin讓業(yè)務(wù)代碼和Dll文件實現(xiàn)關(guān)聯(lián)的目的。
  3. 在各個頁面<head>中,先加載Dll文件,再加載業(yè)務(wù)代碼文件。

適用范圍

Dll文件里只適合放置不常改動的代碼,比如說第三方庫(誰也不會有事無事就升級一下第三方庫吧),尤其是本身就龐大或者依賴眾多的庫。如果你自己整理了一套成熟的框架,開發(fā)項目時只需要在上面添磚加瓦的,那么也可以把這套框架也打包進Dll文件里,甚至可以做到多個項目共用這一份Dll文件。

如何配置哪些代碼需要打包進Dll文件?

我們需要專門為Dll文件建一份webpack配置文件,不能與業(yè)務(wù)代碼共用同一份配置:

const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const dirVars = require('./webpack-config/base/dir-vars.config.js'); // 與業(yè)務(wù)代碼共用同一份路徑的配置表

module.exports = {
  output: {
    path: dirVars.dllDir,
    filename: '[name].js',
    library: '[name]', // 當(dāng)前Dll的所有內(nèi)容都會存放在這個參數(shù)指定變量名的一個全局變量下,注意與DllPlugin的name參數(shù)保持一致
  },
  entry: {
    /*
      指定需要打包的js模塊
      或是css/less/圖片/字體文件等資源,但注意要在module參數(shù)配置好相應(yīng)的loader
    */
    dll: [
      'jquery', '!!bootstrap-webpack!bootstrapConfig',
      'metisMenu/metisMenu.min', 'metisMenu/metisMenu.min.css',
    ],
  },
  plugins: [
    new webpack.DllPlugin({
      path: 'manifest.json', // 本Dll文件中各模塊的索引,供DllReferencePlugin讀取使用
      name: '[name]',  // 當(dāng)前Dll的所有內(nèi)容都會存放在這個參數(shù)指定變量名的一個全局變量下,注意與參數(shù)output.library保持一致
      context: dirVars.staticRootDir, // 指定一個路徑作為上下文環(huán)境,需要與DllReferencePlugin的context參數(shù)保持一致,建議統(tǒng)一設(shè)置為項目根目錄
    }),
    /* 跟業(yè)務(wù)代碼一樣,該兼容的還是得兼容 */
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      'window.$': 'jquery',
    }),
    new ExtractTextPlugin('[name].css'), // 打包css/less的時候會用到ExtractTextPlugin
  ],
  module: require('./webpack-config/module.config.js'), // 沿用業(yè)務(wù)代碼的module配置
  resolve: require('./webpack-config/resolve.config.js'), // 沿用業(yè)務(wù)代碼的resolve配置
};

如何編譯Dll文件?

編譯Dll文件的代碼實際上跟編譯業(yè)務(wù)代碼是一樣的,記得利用--config指定上述專供Dll使用的webpack配置文件就好了:

$ webpack --progress --colors --config ./webpack-dll.config.js

另外,建議可以把該語句寫到npm scripts里,好記一點哈。

如何讓業(yè)務(wù)代碼關(guān)聯(lián)Dll文件?

我們需要在供編譯業(yè)務(wù)代碼的webpack配置文件里設(shè)好DllReferencePlugin的配置項:

new webpack.DllReferencePlugin({
  context: dirVars.staticRootDir, // 指定一個路徑作為上下文環(huán)境,需要與DllPlugin的context參數(shù)保持一致,建議統(tǒng)一設(shè)置為項目根目錄
  manifest: require('../../manifest.json'), // 指定manifest.json
  name: 'dll',  // 當(dāng)前Dll的所有內(nèi)容都會存放在這個參數(shù)指定變量名的一個全局變量下,注意與DllPlugin的name參數(shù)保持一致
});

配置好DllReferencePlugin了以后,正常編譯業(yè)務(wù)代碼即可。不過要注意,必須要先編譯Dll并生成manifest.json后再編譯業(yè)務(wù)代碼;而以后每次修改Dll并重新編譯后,也要重新編譯一下業(yè)務(wù)代碼。

如何在業(yè)務(wù)代碼里使用Dll文件打包的module/資源?

不需要刻意做些什么,該怎么require就怎么require,webpack都會幫你處理好的了。

如何整合Dll?

在每個頁面里,都要按這個順序來加載js文件:Dll文件 => CommonsChunkPlugin生成的公用chunk文件(如果沒用CommonsChunkPlugin那就忽略啦) => 頁面本身的入口文件。

有兩個注意事項:

  • 如果你是像我一樣利用HtmlWebpackPlugin來生成HTML并自動加載chunk的話,請務(wù)必在<head>里手寫<script>來加載Dll文件。
  • 為了完全分離源文件和編譯后生成的文件,也為了方便在編譯前可以清空build目錄,不應(yīng)直接把Dll文件編譯生成到build目錄里,我建議可以先生成到源文件src目錄里,再用file-loader給原封不動搬運過去。

光說不練假把式,來個跑分啊大兄弟!

下面以我的腳手架項目Array-Huang/webpack-seed為例,測試一下(使用開發(fā)環(huán)境的webpack配置文件webpack.dev.config.js)使用這套Dll方案前后的webpack編譯時間:

  • 使用Dll方案前的編譯時間為:10秒17
  • 使用Dll方案后的編譯時間為:4秒29

由于該項目只是一個腳手架,涉及到的第三方庫并不多,我只把jQuery、bootstrap、metisMenu給打包進Dll文件里了,盡管如此,還是差了將近6秒了,相信在實際項目中,這套DllPlugin&DllReferencePlugin的方案能為你省下更多的時間來找女朋友(大誤)。

示例代碼

諸位看本系列文章,搭配我在Github上的腳手架項目食用更佳哦(笑):Array-Huang/webpack-seedhttps://github.com/Array-Huang/webpack-seed)。

附系列文章目錄(同步更新)

本文首發(fā)于Array_Huang的技術(shù)博客——實用至上,非經(jīng)作者同意,請勿轉(zhuǎn)載。
原文地址:https://segmentfault.com/a/1190000007104372
如果您對本系列文章感興趣,歡迎關(guān)注訂閱這里:https://segmentfault.com/blog/array_huang

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容