深入理解基于vue-cli的webpack打包优化实践及探索

作者:知秋时间:2026-10-23 11:59:19浏览:489

本文深入探讨了基于vue-cli的webpack打包优化实践。通过speed-measure-webpack-plugin和webpack-bundle-analyzer进行测速与体积分析,尝试了缩小文件查找范围、使用HappyPack多线程打包以及DllPlugin提取第三方库等策略。其中,HappyPack因与vue-loader不兼容导致失败,而DllPlugin通过分离大型第三方库显著将打包时间从30秒降至20秒左右,为前端构建优化提供了具体参考。

时间进入2019年,短短三四年间,webpack打包工具已在前端开发中成为必备技能。过去面试常问页面优化手段,如缓存、文件压缩、CSS雪碧图及CDN部署等,如今问题更多转向webpack的打包原理与优化方法。笔者出于兴趣研究webpack打包优化,虽市面上已有类似文章,但仍希望提供新的视角与启发。

准备工作:测速与分析bundle

1、准备工作:测速与分析bundle

优化webpack打包前,需先对bundle文件进行分析,明确各模块大小及打包耗时环节。主要依赖两个webpack插件:speed-measure-webpack-plugin用于测速,webpack-bundle-analyzer用于分析bundle文件内容。

具体配置


const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin({
 outputFormat:"human",
});
module.exports = {
configureWebpack: smp.wrap({
  plugins: [
   new webpack.ProvidePlugin({
    $: "zepto",
    Zepto: "zepto",
   }),
   new BundleAnalyzerPlugin(),
  ],
  optimization: {
   splitChunks: {
    cacheGroups: {
     echarts: {
      name: "chunk-echarts",
      test: /[\\/]node_modules[\\/]echarts[\\/]/,
      chunks: "all",
      priority: 10,
      reuseExistingChunk: true,
      enforce: true,
     },
     demo: {
      name: "chunk-demo",
      test: /[\\/]src[\\/]views[\\/]demo[\\/]/,
      chunks: "all",
      priority: 20,
      reuseExistingChunk: true,
      enforce: true,
     },
     page: {
      name: "chunk-page",
      test: /[\\/]src[\\/]/,
      chunks: "all",
      priority: 10,
      reuseExistingChunk: true,
      enforce: true,
     },
     vendors: {
      name: "chunk-vendors",
      test: /[\\/]node_modules[\\/]/,
      chunks: "all",
      priority: 5,
      reuseExistingChunk: true,
      enforce: true,
     },
    },
   },
  },
 })
}

基于vue-cli脚手架的项目已内置部分优化,如配置splitchunk进行代码分割。这在大型项目中至关重要,避免用户加载过大的JS文件(如5MB)导致阻塞,因此代码分割和懒加载是必要的。

在配置中,需用smp对配置进行包裹,因为SpeedMeasurePlugin会包裹其他Plugin对象以记录其开始和结束时间。

其次,BundleAnalyzerPlugin作为普通plugin,加载在plugins数组的后面即可。

接下来我们看一下最初的打包时间以及包内容分析:

准备工作:测速与分析bundle

准备工作:测速与分析bundle

从分析结果可见,项目中体积较大的三个包中,有两个属于第三方依赖,如three.js、lottie、lodash、echarts等。

开始逐步优化

2、开始逐步优化

2.1缩小文件查找和处理范围

这是webpack优化的常规操作,旨在优化模块和文件查找,减少loader对不必要模块的处理。vue-cli未暴露内置loader供直接优化,但其配置项已对loader查找路径进行了优化。若项目使用vue-cli,可通过命令行查看现有配置文件:


npx vue-cli-service inspect > output.js

具体可查阅vuecli官方文档。


resolve:{
 modules: [path.resolve(__dirname, 'node_modules')],
 alias:{
  'three':path.resolve(__dirname, './node_modules/three/build/three.min.js'),
  'zepto$':path.resolve(__dirname, './node_modules/zepto/dist/zepto.min.js'),
  'swiper$':path.resolve(__dirname, './node_modules/swiper/dist/js/swiper.min.js'),
  'lottie-web$':path.resolve(__dirname, './node_modules/lottie-web/build/player/lottie.min.js'),
  'lodash$':path.resolve(__dirname, './node_modules/lodash/lodash.min.js'),
 }
},
module:{
 noParse:/^(vue|vue-router|vuex|vuex-router-sync|three|zepto|swiper|lottie-web|lodash)$/
},
  • 通过modules指定查找第三方模块的路径。
  • 通过alias指定直接查找到打包构建好的压缩js文件。
  • 通过module指定noparse,对第三方模块不再进行依赖分析。

优化效果:2s?

准备工作:测速与分析bundle

结果显示打包时间减少了两三秒,波动在30s左右,感觉差异不大。

2.2尝试使用happypack

在优化前查阅了大量webpack优化文章,笔者尝试使用happypack优化打包时间。关于happypack的效果主要有两种说法:

1、webpack4中已默认支持多线程打包,因此happypack效果不明显;

2、vue不支持happypack打包,需设置thread-loader。

尽管如此,笔者仍决定尝试,至少只对JS和CSS文件设置happypack。

然而问题随之而来,vue-cli内置封装了loader,如何获取其配置并改写其中的loader配置成为难题。

通过查阅vue-cli官方文档,可以看到以下使用介绍:

configureWebpack Type: Object | Function 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中。 如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。

为此,笔者特地调试进了vue-cli的源码一探究竟:

流程介绍:

执行命令行vue-cli-service build时,系统先去node_modules的.bin文件夹下查找相应的可执行文件,.bin下的vue-cli-service会映射到相应的第三方库内的执行文件。

因此我们可以找到这个可执行文件的地址:

/node_modules/@vue/cli-service/bin/vue-cli-service.js

找到入口后,若想进入nodejs调试,以往通过node --inspect app.js启动后台服务,然后在谷歌浏览器里进入调试界面(F12选择绿色的那个小按钮)。

但此处遇到难题,因为打包构建是一次性执行,不同于实时监听的后台服务。查阅后发现,调试普通nodejs文件需通过以下方式:


node --inspect-brk=9229 app.js

因此,为了强行进入vue-cli源码调试,查看其处理流程,需输入以下命令行:


node --inspect-brk=9229 node_modules/@vue/cli-service/bin/vue-cli-service.js build

上述命令行等价于vue-cli-service build。

通过这种方式,终于进入了vue-cli源码,查看其执行流程,可在对应位置打下断点,查看此时作用域内的变量数据。

准备工作:测速与分析bundle

可以看到vue-cli源码中的这一段操作,会执行传入的函数,根据函数是否有返回值决定是否merge进其内部配置的config。

通过这段代码可以看出,若将configWepack配置为函数,通过参数形式获取config配置项(本身是对象,保留引用),直接修改传入的config对象即可实现目标:修改vue-cli内置的loader!

当然,除了断点查看配置,也可通过命令行输出为output文件查看现有配置。

这里给大家截图看一下vue-cli内部的配置:

准备工作:测速与分析bundle

可能有些啰嗦,但通过断点方式可以看到,vue-cli其实已对js文件设置了exclude,同时也配置好了cache-loader,意味着webpack常规优化方式之一的cache-loader缓存也已被处理。

回到最初目标,处理JS和CSS的loader,模仿多数配置进行了以下修改:


 configureWebpack:(config)=>{
  console.log("webpack config start");
  let originCssRuleLoader = config.module.rules[6].oneOf[0].use;
  let newCssRuleLoader = 'happypack/loader?id=css';
  config.module.rules[6].oneOf[0].use = newCssRuleLoader
  config.module.rules[6].oneOf[1].use = newCssRuleLoader
  config.module.rules[6].oneOf[2].use = newCssRuleLoader
  config.module.rules[6].oneOf[3].use = newCssRuleLoader
  ...//other code
 }

尝试对css的loader配置进行修改。之后对plugins进行一下配置:


plugins: [
  new HappyPack({
   id: 'css',
   threads: 4,
   loaders: originCssRuleLoader
  }),
 ],

本以为这样就OK了,但很遗憾地告诉大家,报错了...

准备工作:测速与分析bundle

可以看到报错内容,是在处理vue文件时出了错误。

如何解决

笔者通过百度和谷歌搜索,大抵说法是happypack不支持vue-loader。同时根据报错查询处理方案,设置parallel参数也无效。

笔者甚至怀疑配置有误,于是将配置原样移植到另一个非vue项目中,一切运行正常。 答案:此题无解~

原因分析:

由于vue文件中含有CSS,vue-loader会提取其中的css交给其他loader处理,vue-loader-plugin通过在vue文件后加查询字符串告知其他loader对该文件进行处理。这意味着vue-loader处理文件时通知其他loader,但此时loader配置已被改写为happypack,而vue与happypack不兼容,最终导致报错。很遗憾地告诉大家,vue-cli接入happypack--失败。

(注:这一部分主要是笔者在webpack优化过程中的探索,虽然最终未能很好地优化webpack打包,但在此探索过程中也能学到很多~包括 vue-cli对配置对象的处理?如何调试普通文件nodejs代码?vue-loader中对vue文件的处理流程?vue-loader-plugin帮我们做了什么事?而这些都是要自己慢慢翻阅,慢慢踩坑去了解的~)

使用dllplugin

2.3使用dllplugin

与大多数webpack优化教程一样,笔者也尝试利用dllplugin进行优化。该插件本质是提取常用的第三方模块,单独打成一个文件包,之后插入到html页面中。这样每次打包无需再处理第三方模块,毕竟第三方模块动辄成千上万行。

流程介绍:

1、配置webpack.dll.js针对第三方库打包

2、vue.config.js中配置plugin

3、html中引入dll打包出来的js文件。(一般采用部署CDN的方式)

由于项目中有很多大型第三方库,如three、echart等,因此进行了以下配置:(webpack.dll.js)


const webpack = require("webpack")
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
    vuebundle: [
      'vue',
      'vue-router',
      'vuex',
    ],
    utils:[
      'lodash',
      'swiper',
      'lottie-web',
      'three',
    ],
    echarts:[
      'echarts/lib/echarts',
      "echarts/lib/chart/bar",
      "echarts/lib/chart/line",
      "echarts/lib/component/tooltip",
      "echarts/lib/component/title",
      "echarts/lib/component/legend",
    ]

  },
  output: {
    path: path.resolve(__dirname, './static/'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, 'build', '[name]-manifest.json'),
      name: '[name]_library'
    })
  ]
}

针对不同库的大小进行划分,打了三个包。为何不打成一个包?一个包会太大,用户加载大型JS文件包会阻塞,影响页面性能。

接下来是vue.config.js的配置:


plugins: [
   new webpack.ProvidePlugin({
    $: "zepto",
    Zepto: "zepto",
   }),
   new DllReferencePlugin({
    manifest: require('./build/echarts-manifest.json'),
   }),
   new DllReferencePlugin({
    manifest: require('./build/utils-manifest.json'),
   }),
   new DllReferencePlugin({
    manifest: require('./build/vuebundle-manifest.json'),
   }),
   new BundleAnalyzerPlugin(),
  ]

引入了DllPlugin。接下来配置HTML:

(由于笔者没将DLL打包出来的js文件上传到CDN,所以只能本地自己起个node服务器返回静态资源了)


 <body>
   <div id="app"></div>
  <!-- built files will be auto injected -->
  <script type="text/javascript" src="http://localhost:3000/echarts.dll.js"></script>
  <script type="text/javascript" src="http://localhost:3000/utils.dll.js"></script>
  <script type="text/javascript" src="http://localhost:3000/vuebundle.dll.js"></script>
 </body>

然后npm run serve,开始页面调试和开发~

舒服~

优化结果:

准备工作:测速与分析bundle

由于少了大型第三方库,时间控制在了20s左右。优化相对比较明显~

优化与探索总结

3、优化与探索总结

优化至此基本结束。

webpack常见优化方式包括优化路径查找、设置缓存、happypack以及dllplugin。前两项vue-cli已做部分处理,happypack因与vue不兼容无法接入,dllplugin通过单独提取第三方库取得了明显优化。 此外,笔者也尝试剔除项目中无用代码,但效果不痛不痒。

webpack优化方式总结:

1、优化模块查找路径

2、剔除不必要的无用的模块

3、设置缓存:缓存loader的执行结果(cacheDirectory/cache-loader)

4、设置多线程:HappyPack/thread-loader

5、dllplugin提取第三方库

当然,这是针对开发的优化。若针对部署优化,可设置splitchunk、按需加载、部署CDN等,此处不展开。

最后

希望这篇文章能对大家有所收获~ webpack已是前端必备技能~有空钻研webpack配置和原理,会有所收获!谢谢观看~

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

作者最新文章