webpack dll打包重復(fù)問(wèn)題優(yōu)化的解決
關(guān)于webpack dll的使用,我這里不做過(guò)多介紹,網(wǎng)上都有,一擼一大把,今天我要說(shuō)的是在使用dll plugin過(guò)程中出現(xiàn)的一個(gè)包依賴(lài)問(wèn)題,這個(gè)問(wèn)題導(dǎo)致打出來(lái)的包會(huì)包含重復(fù)的代碼。
優(yōu)化背景
最近在給公司項(xiàng)目?jī)?yōu)化的時(shí)候,由于 內(nèi)部CDN上傳文件大小限制了500K ,所以用了webpack dll來(lái)進(jìn)行拆分打包,我將拆分的包分為三部分:
- vue生態(tài)包(
vue
、vuex
、vue-router
、vuex-class
、vue-class-component
等周邊生態(tài)的庫(kù)) - vue插件包(
vee-validate
、內(nèi)部UI庫(kù),圖片預(yù)覽等vue插件庫(kù)) - 第三方包(
axios
、內(nèi)部一些錯(cuò)誤統(tǒng)計(jì)、上報(bào),員工水印等這些脫離于vue的第三方庫(kù))
三部分的包名分別是 vue.dll.js
、 plugin.dll.js
、 lib.dll.js
,這樣的好處是結(jié)構(gòu)清晰,最重要的原因還是分解包的大小,降低到500K以?xún)?nèi)
但是在進(jìn)行dll打包后,我驚奇地發(fā)現(xiàn) vue.dll.js
和 plugin.dll.js
中會(huì)包含重復(fù)的vue的dist代碼
下面是分別是前兩部分的bundle分析圖
可以看到這倆dll都包含了vue
那么要分析問(wèn)題原因,先說(shuō)一下我的DLL的配置吧
DLL配置
因?yàn)閣ebpack支持多entry,所以一般多入口dll打包的話(huà),首先會(huì)考慮一個(gè)webpack配置,多個(gè)entry入口,所以可能會(huì)出現(xiàn)
// webpack.dll.conf.js module.exports = { // 其他配置先省略 entry: { vue: ['vue', 'vuex', 'vue-router', ...], plugin: ['vee-validate', '內(nèi)部UI庫(kù)', ...], lib: ['axios', 'dayjs', ...] }, plugins: [ new webpack.DllPlugin({ // dll.配置 }) ] }
但是親測(cè)這樣打包出來(lái)的文件依然有上述問(wèn)題
所以結(jié)合我在之前公司所實(shí)踐的 webpack multi compiler 方式,參考webpack multi compiler ,我把webpack的配置一分為三,每一個(gè)dll包都有一個(gè)webpack配置,即
// config.js exports.dll = [ { name: 'vue', libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component'] }, { name: 'lib', libs: [axios', 'dayjs', '第三方庫(kù)'] }, { name: 'plugin', libs: ['vee-validate', 'v-viewer', 'vue插件庫(kù)'] } ]
// webpack.dll.conf.js module.exports = config.dll.map(function (vendor) { return { // 省略其他配置 entry: { [vendor.name]: vendor.libs }, plugins: [ new webpack.DllPlugin({ // dll.配置 }) ] } })
// dll.js const dllConfig = require('./webpack.dll.conf') webpack(dllConfig, function (err, stats) { if (err) throw err // 處理stats相關(guān)信息 })
本以為這樣可以解決問(wèn)題,但是現(xiàn)實(shí)卻是不能,所以得先分析一下問(wèn)題所在
分析問(wèn)題
經(jīng)過(guò)仔細(xì)的排查,發(fā)現(xiàn)是由于內(nèi)部UI庫(kù)中單獨(dú)引用了vue,即在庫(kù)中有
import Vue from 'vue' // ... // Vue相關(guān)操作 // Vue.prototype.$isServer等
這樣不管是多入口打包還是multi compiler方式下都會(huì)出現(xiàn)重復(fù)的包
解決方法
分析dll的原理,其實(shí)dll在打包的時(shí)候會(huì)將所有包含的庫(kù)做一個(gè)索引,寫(xiě)在一個(gè)manifest文件中,然后在引用dll的時(shí)候只需要引用這個(gè)manifest文件即可
所以我就在想,如果plugin.dll.js依賴(lài)于vue.dll.js中的vue,那么是否可以先打包vue.dll.js,然后在打包plugin.dll.js的時(shí)候引用vue.dll.js呢?
心動(dòng)不如行動(dòng),趕緊嘗試一下,做出如下修改
// config.js exports.dll = [ { name: 'vue', libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component'] }, { name: 'lib', libs: [axios', 'dayjs', '第三方庫(kù)'] }, { name: 'plugin', libs: ['vee-validate', 'v-viewer', 'vue插件庫(kù)'], ref: 'vue' } ]
// webpack.dll.conf.js // generate config const gen = function (vendors) { return vendors.map(function (item) { const base = { entry: { [item.name]: item.libs }, plugins: [ new webpack.DllPlugin({ // dll配置 }) ] } if (item.ref) { // 重點(diǎn)在這 // 在有ref的dll配置中,插入dll reference的plugin,內(nèi)容是所依賴(lài)的dll包的manifest base.plugins.push(new webpack.DllReferencePlugin({ // dll reference其他配置 manifest: '所依賴(lài)的dll包的manifest文件路徑' })) } return base }) } // 根據(jù)是否有ref依賴(lài)項(xiàng),區(qū)分base config和ref config const [baseVendors, refVendors] = config.dll.vendors.reduce((config, v) => { config[v.ref ? 1 : 0].push(v) return config }, [ [], [] ]) // 生成base config const getConfig = function () { return gen(baseVendors) } // 生成ref config const getRefConfig = function () { return gen(refVendors) } module.exports = { getConfig, getRefConfig }
// dll.js const dllConfig = require('./webpack.dll.conf') // 因?yàn)閞ef config依賴(lài)于base config,所以要保證base config先打包出來(lái) const runWebpack = function (config) { return new Promise(function (resolve) { webpack(config, function (err, stats) { if (err) throw err // ... resolve() }) }) } module.exports = function run () { runWebpack(dllConfig.getConfig()) .then(() => runWebpack(dllConfig.getRefConfig())) }
整體變成了如下結(jié)構(gòu)
最關(guān)鍵的一步就是plugin.dl.js會(huì)引用vue.dll.js的manifest文件,這樣公共部分vue,就只會(huì)出現(xiàn)在vue.dll.js中了,plugin.dll.js打包后的bundle分析圖如下
可以很明顯地看到plugin.dll.js中已經(jīng)沒(méi)有vue dist的身影了,包的體積得到了優(yōu)化:v:
可優(yōu)化項(xiàng)
上述優(yōu)化其實(shí)只考慮了一個(gè)依賴(lài)項(xiàng),那么如果plugin.dll.js同時(shí)依賴(lài)于vue.dll.js和lib.dll.js呢?如果此時(shí)vue.dll.js也依賴(lài)于lib.dll.js呢?
如果出現(xiàn)上述情況,那么請(qǐng)先考慮dll包是否需要拆分?拆分是否合理?
然后再思考如何根據(jù)依賴(lài)順序思考打包順序,以及如果出現(xiàn)循環(huán)依賴(lài),該怎么辦?
由于目前優(yōu)化需求中還未出現(xiàn)這種情況(這種情況應(yīng)該很少很少很少見(jiàn)),所以我這邊就沒(méi)有解決這些問(wèn)題了
總結(jié)
參考平常打包通過(guò)dll reference plugin來(lái)引用dll包的manifest的方式,如果多個(gè)dll包內(nèi)出現(xiàn)了依賴(lài),導(dǎo)致打包重復(fù),那么是可以在依賴(lài)包中運(yùn)用dll reference plugin來(lái)引用被依賴(lài)包的dll manifest,不過(guò)這樣的話(huà),需要注意dll包的打包順序,被依賴(lài)包的dll要先于依賴(lài)包dll進(jìn)行打包
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
解決JavaScript數(shù)字精度丟失問(wèn)題的方法
這篇文章主要介紹了解決JavaScript數(shù)字精度丟失問(wèn)題的方法,需要的朋友可以參考下2015-12-12?javascript學(xué)數(shù)組中的foreach方法和some方法
這篇文章主要介紹了?javascript學(xué)數(shù)組中的foreach方法和some方法,文章相關(guān)內(nèi)容和代碼詳細(xì),具有一定的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的學(xué)習(xí)有所幫助2022-03-03js控制div及網(wǎng)頁(yè)相關(guān)屬性的代碼
js控制div及相關(guān)屬性,對(duì)于需要控制頁(yè)面內(nèi)的元素的朋友可以參考下。2009-12-12js Array對(duì)象的擴(kuò)展函數(shù)代碼
有時(shí)候我們需要對(duì)js的array對(duì)象擴(kuò)展一些功能,這里簡(jiǎn)單介紹下,方便需要的朋友2013-04-04關(guān)于javascript DOM事件模型的兩件事
DOM事件模型的兩件事:事件捕捉(Event Capture)的實(shí)現(xiàn)問(wèn)題以及IE的高級(jí)事件處理模型的問(wèn)題。2010-07-07淺談javascript六種數(shù)據(jù)類(lèi)型以及特殊注意點(diǎn)
這篇文章主要介紹了javascript六種數(shù)據(jù)類(lèi)型以及特殊注意點(diǎn),有需要的朋友可以參考一下2013-12-12