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