本文针对Webpack DLL Plugin打包过程中出现的代码重复问题,深入分析了多入口与Multi Compiler模式的局限性。通过识别内部UI库对Vue的单独引用,提出在依赖DLL中引入DllReferencePlugin引用被依赖DLL的manifest文件。核心解决方案在于严格控制打包顺序,确保被依赖包先于依赖包生成,从而消除冗余代码,优化包体积。
关于Webpack DLL的使用,网上已有大量介绍,此处不再赘述。本文将重点探讨在使用Dll Plugin时遇到的一个包依赖问题,该问题会导致最终生成的包中包含重复代码。
优化背景
优化背景与分包策略
近期在公司项目优化过程中,由于内部CDN对上传文件大小限制为500K,因此采用Webpack DLL进行拆分打包。我们将拆分的包划分为以下三部分:
- Vue生态包(包含
vue、vuex、vue-router、vuex-class、vue-class-component等周边生态库) - Vue插件包(包含
vee-validate、内部UI库、图片预览等Vue插件库) - 第三方包(包含
axios、内部错误统计、上报、员工水印等独立于Vue的第三方库)
这三部分包的名称分别为 vue.dll.js 、 plugin.dll.js 、 lib.dll.js 。这样划分的好处在于结构清晰,更重要的是能将每个分解包的大小控制在500K以内。
然而,在进行DLL打包后,发现 vue.dll.js 和 plugin.dll.js 中均包含了重复的Vue dist代码。
以下是前两个包的Bundle分析图。


从图中可以看出,这两个DLL都包含了Vue。
为了分析原因,先介绍我的DLL配置方式。
DLL配置与问题复现
DLL配置
由于Webpack支持多Entry,通常多入口DLL打包会考虑使用一个Webpack配置配合多个Entry入口,这可能导致如下配置:
// webpack.dll.conf.js
module.exports = {
// 其他配置先省略
entry: {
vue: ['vue', 'vuex', 'vue-router', ...],
plugin: ['vee-validate', '内部UI库', ...],
lib: ['axios', 'dayjs', ...]
},
plugins: [
new webpack.DllPlugin({
// dll.配置
})
]
}
但经过实测,这样打包出来的文件依然存在上述重复问题。
因此,结合之前在公司的实践,采用Webpack Multi Compiler方式,参考相关文档,我将Webpack配置拆分为三个独立配置,每个DLL包对应一个Webpack配置,即:
// config.js
exports.dll = [
{
name: 'vue',
libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component']
},
{
name: 'lib',
libs: [axios', 'dayjs', '第三方库']
},
{
name: 'plugin',
libs: ['vee-validate', 'v-viewer', 'vue插件库']
}
]
// 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相关信息
})
本以为这样可以解决问题,但实际情况并非如此,因此需要进一步分析问题所在。
问题根源分析
分析问题
经过仔细排查,发现原因是内部UI库中单独引用了Vue,即在库中存在如下代码:
import Vue from 'vue'
// ...
// Vue相关操作
// Vue.prototype.$isServer等
因此,无论是多入口打包还是Multi Compiler方式,都会出现重复包的问题。
基于依赖关系的解决方案
解决方法
分析DLL原理可知,DLL打包时会将所有包含的库建立索引,写入manifest文件。引用DLL时只需引用该manifest文件即可。
于是思考:如果plugin.dll.js依赖于vue.dll.js中的Vue,是否可以先打包vue.dll.js,然后在打包plugin.dll.js时引用vue.dll.js?
说干就干,尝试进行如下修改:
// config.js
exports.dll = [
{
name: 'vue',
libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component']
},
{
name: 'lib',
libs: [axios', 'dayjs', '第三方库']
},
{
name: 'plugin',
libs: ['vee-validate', 'v-viewer', 'vue插件库'],
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) {
// 重点在这
// 在有ref的dll配置中,插入dll reference的plugin,内容是所依赖的dll包的manifest
base.plugins.push(new webpack.DllReferencePlugin({
// dll reference其他配置
manifest: '所依赖的dll包的manifest文件路径'
}))
}
return base
})
}
// 根据是否有ref依赖项,区分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')
// 因为ref config依赖于base config,所以要保证base config先打包出来
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()))
}
整体结构变为如下形式。

最关键的一步是plugin.dll.js会引用vue.dll.js的manifest文件,这样公共部分Vue就只会出现在vue.dll.js中。plugin.dll.js打包后的Bundle分析图如下:

可以明显看到,plugin.dll.js中已无Vue dist代码,包体积得到优化:
复杂依赖场景与总结
可优化项
上述优化仅考虑了一个依赖项。如果plugin.dll.js同时依赖于vue.dll.js和lib.dll.js,且vue.dll.js也依赖于lib.dll.js,该如何处理?
出现上述情况时,请先考虑DLL包是否需要拆分,以及拆分是否合理。
然后思考如何根据依赖顺序确定打包顺序,以及如何处理循环依赖。
由于当前优化需求中尚未出现这种情况(这种情况极少见),因此未对此进行解决。
总结
参考平常通过Dll Reference Plugin引用DLL包manifest的方式,如果多个DLL包内出现依赖导致打包重复,可以在依赖包中运用Dll Reference Plugin引用被依赖包的DLL manifest。但需注意DLL包的打包顺序,被依赖包的DLL需先于依赖包DLL进行打包。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。