摘要:本文首發于的技術博客實用至上,非經作者同意,請勿轉載。原文地址如果您對本系列文章感興趣,歡迎關注訂閱這里前言書承上文多頁應用架構系列十如何打造一個自定義的。終于,發現了這一大殺器,打包時間過長的問題得到完美解決。
本文首發于Array_Huang的技術博客——實用至上,非經作者同意,請勿轉載。前言
原文地址:https://segmentfault.com/a/1190000007104372
如果您對本系列文章感興趣,歡迎關注訂閱這里:https://segmentfault.com/blog/array_huang
書承上文《webpack多頁應用架構系列(十):如何打造一個自定義的bootstrap》。
上文說到我們利用webpack來打包一個可配置的bootstrap,但文末留下一個問題:由于bootstrap十分龐大,因此每次編譯都要耗費大部分的時間在打包bootstrap這一塊,而換來的僅僅是配置的便利,十分不劃算。
我也并非是故意賣關子,這的確是我自己開發中碰到的問題,而在撰寫完該文后,我立即著手探索解決之道。終于,發現了webpack這一大殺器:DllPlugin&DllReferencePlugin,打包時間過長的問題得到完美解決。
解決方案的機制和原理DllPlugin&DllReferencePlugin這一方案,實際上也是屬于代碼分割的范疇,但與CommonsChunkPlugin不一樣的是,它不僅僅是把公用代碼提取出來放到一個獨立的文件供不同的頁面來使用,它更重要的一點是:把公用代碼和它的使用者(業務代碼)從編譯這一步就分離出來,換句話說,我們可以分別來編譯公用代碼和業務代碼了。這有什么好處呢?很簡單,業務代碼常改,而公用代碼不常改,那么,我們在日常修改業務代碼的過程中,就可以省出編譯公用代碼那一部分所耗費的時間了(是不是馬上就聯想到坑爹的bootstrap了呢)。
整個過程大概是這樣的:
利用DllPlugin把公用代碼打包成一個“Dll文件”(其實本質上還是js,只是套用概念而已);除了Dll文件外,DllPlugin還會生成一個manifest.json文件作為公用代碼的索引供DllReferencePlugin使用。
在業務代碼的webpack配置文件中配置好DllReferencePlugin并進行編譯,達到利用DllReferencePlugin讓業務代碼和Dll文件實現關聯的目的。
在各個頁面
中,先加載Dll文件,再加載業務代碼文件。 適用范圍Dll文件里只適合放置不常改動的代碼,比如說第三方庫(誰也不會有事無事就升級一下第三方庫吧),尤其是本身就龐大或者依賴眾多的庫。如果你自己整理了一套成熟的框架,開發項目時只需要在上面添磚加瓦的,那么也可以把這套框架也打包進Dll文件里,甚至可以做到多個項目共用這一份Dll文件。
如何配置哪些代碼需要打包進Dll文件?我們需要專門為Dll文件建一份webpack配置文件,不能與業務代碼共用同一份配置:
const webpack = require("webpack"); const ExtractTextPlugin = require("extract-text-webpack-plugin"); const dirVars = require("./webpack-config/base/dir-vars.config.js"); // 與業務代碼共用同一份路徑的配置表 module.exports = { output: { path: dirVars.dllDir, filename: "[name].js", library: "[name]", // 當前Dll的所有內容都會存放在這個參數指定變量名的一個全局變量下,注意與DllPlugin的name參數保持一致 }, entry: { /* 指定需要打包的js模塊 或是css/less/圖片/字體文件等資源,但注意要在module參數配置好相應的loader */ dll: [ "jquery", "!!bootstrap-webpack!bootstrapConfig", "metisMenu/metisMenu.min", "metisMenu/metisMenu.min.css", ], }, plugins: [ new webpack.DllPlugin({ path: "manifest.json", // 本Dll文件中各模塊的索引,供DllReferencePlugin讀取使用 name: "[name]", // 當前Dll的所有內容都會存放在這個參數指定變量名的一個全局變量下,注意與參數output.library保持一致 context: dirVars.staticRootDir, // 指定一個路徑作為上下文環境,需要與DllReferencePlugin的context參數保持一致,建議統一設置為項目根目錄 }), /* 跟業務代碼一樣,該兼容的還是得兼容 */ 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"), // 沿用業務代碼的module配置 resolve: require("./webpack-config/resolve.config.js"), // 沿用業務代碼的resolve配置 };如何編譯Dll文件?
編譯Dll文件的代碼實際上跟編譯業務代碼是一樣的,記得利用--config指定上述專供Dll使用的webpack配置文件就好了:
$ webpack --progress --colors --config ./webpack-dll.config.js
另外,建議可以把該語句寫到npm scripts里,好記一點哈。
如何讓業務代碼關聯Dll文件?我們需要在供編譯業務代碼的webpack配置文件里設好DllReferencePlugin的配置項:
new webpack.DllReferencePlugin({ context: dirVars.staticRootDir, // 指定一個路徑作為上下文環境,需要與DllPlugin的context參數保持一致,建議統一設置為項目根目錄 manifest: require("../../manifest.json"), // 指定manifest.json name: "dll", // 當前Dll的所有內容都會存放在這個參數指定變量名的一個全局變量下,注意與DllPlugin的name參數保持一致 });
配置好DllReferencePlugin了以后,正常編譯業務代碼即可。不過要注意,必須要先編譯Dll并生成manifest.json后再編譯業務代碼;而以后每次修改Dll并重新編譯后,也要重新編譯一下業務代碼。
如何在業務代碼里使用Dll文件打包的module/資源?不需要刻意做些什么,該怎么require就怎么require,webpack都會幫你處理好的了。
如何整合Dll?在每個頁面里,都要按這個順序來加載js文件:Dll文件 => CommonsChunkPlugin生成的公用chunk文件(如果沒用CommonsChunkPlugin那就忽略啦) => 頁面本身的入口文件。
有兩個注意事項:
如果你是像我一樣利用HtmlWebpackPlugin來生成HTML并自動加載chunk的話,請務必在
里手寫