Webpack DLL 打包重复代码问题:基于依赖顺序的优化方案

作者:构建工坊时间:2026-10-22 18:12:58浏览:214

本文针对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进行打包。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

作者最新文章
相关文章
更多