webpack优化vue-Cli的webpack打包优化实践与探索

一转眼已经是2019年了,短短三四年时间,webpack打包工具已经成为后端开发必备工具。 面试题都讲完了,后端页面优化的方法有哪些? 大家可以轻松地谈论缓存、压缩文件、CSS精灵图像、CDN部署等各种技术,但昨晚有所不同。 也许你去笔试会问,你知道webpack的打包原理吗,webpack的打包优化方法有哪些? 那我该怎么说呢? 作者无意研究webpack的打包优化。 也许你读过类似的优化文章~但作者还是希望能给你一些新的启发~

1. 准备工作:测速与分析包

既然我们要优化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脚手架,虽然vue-cli已经帮你做了一些优化工作,但是可以看出原项目初始配置设置了splitchunk进行代码分割,这在小项目中是非常有必要的,毕竟你不希望你的用户阻塞加载一个5MB的JS文件,所以代码分割和延迟加载是必要的。

扯远了,我们来看看这个配置。 您需要使用 smp 重新打包配置,因为 SpeedMeasurePlugin 将为您的其他 Plugin 对象包装一层代理。 这样做的目的是为了知道插件什么时候开始和结束~

其次,BundleAnalyzerPlugin 就像普通插件一样,只需将其加载到插件链接列表旁边即可。

接下来我们看一下初始打包时间和打包内容分析:

可以看到项目中三个较大的包,其中两个是我们的第三方依赖, Three.js、lottie、lodash、echarts等。

2.开始逐步优化

2.1 缩小文件搜索和处理的范围

这是webpack优化中的常规操作。 基本上是优化模块和文件搜索,减少加载器对一些不必要模块的处理。 但是 vue-cli 中的 loader 并没有暴露给我们操作,所以它的外部 loader 我们很难优化处理,但毕竟 vue-cli 中的配置项已经优化了 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)$/
},

优化疗效:2s?

可以看到时间减少了两三秒,在30s上下波动,并没有太大的差别。

2.2 尝试使用happypack

由于在webpack优化之前看了很多关于webpack优化的文章,所以我也想尝试使用happypack来优化打包时间。 想要用happypack打包之前,大致有两种说法:

1、webpack4中已经默认采用多线程打包,所以happypack打包效果并不显着;

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

不过笔者想了想,还是试一试吧,大不了我只为JS和CSS文件设置happypack。

但是问题又来了,vue-cli外部封装了loader,这个时候怎么获取它的配置,并重写上面的loader配置。

通过阅读vue-cli的官方文档,我们可以看到如下介绍:

配置Webpack类型:对象| 作用 如果这个值是一个对象,它会被 webpack-merge 合并到最终的配置中。 如果该值是一个函数,它将接收解析的配置作为参数。 此函数可以更改配置并且不返回任何内容,也可以返回配置的克隆或合并版本。

为此,笔者专门调试进入vue-cli的源码发现:

流程介绍:

由于我们执行命令行vue-cli-service构建,实际上是去node_modules的.bin文件夹中查找对应的可执行文件,.bin下的vue-cli-service会映射到对应的第三个中执行-方库文档。

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

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

找到入口后,我们要步入nodejs的调试。 往年的开发中,我们都会以node --inspect app.js的形式启动一个后台服务,然后在Google Chrome中进入调试界面(F12选择红色小按钮)

但是这里有一个困难,因为我们的包创建是执行一次的,这与后台服务不同webpack优化vue,后台服务是实时点击的,服务仍然在运行。 看看吧,如果你想调试普通的nodejs文件,需要使用这个方法:

node --inspect-brk=9229 app.js

因此,为了强制进入vue-cli的源码进行调试,我们可以看到vue-cli的处理流程,我们需要像这样输入以下命令行:

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

上面的命令行相当于vue-cli-service build。

通过这种方法,我们最终进入了vue-cli的源码,看到它的执行过程后,就可以在相应的位置设置断点来查看此时作用域内的变量数据了。

可以看到vue-cli源码中的这一段操作会执行我们传入的函数,并判断函数是否有返回值来决定是否合并到其内部配置config中。

通过这段代码我们可以看到,如果我们将configWepack配置为函数,然后通过参数获取config配置项,它本身就是一个对象,而对象是一种保留引用的方式,所以如果我们直接修改传入的config object 改变一下,就可以达到我们原来的目的了! 修改vue-cli的外部加载器!

当然,除了单步进入断点查看配置之外,正如我刚才所说,我们还可以通过命令行输出作为输出文件来查看现有的配置。

这里可以截图看看vue-cli的内部配置:

webpack优化vue_优化webpack_webpack

可能有点废话了,但是通过断点的形式我们可以看到,虽然vue-cli已经对js文件设置了exclusion,但是它也帮我们设置了cache-loader,也就是说常规的优化方式之一webpack的就是使用cache-loader也为我们缓存了它。

回到最初的起点,我们要处理的是JS和CSS的加载器,所以模仿大部分配置,我做了以下修改:

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 的加载器配置。 然后配置插件:

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

我以为这样就可以了,但是很遗憾地告诉你,报错了……

可以看到错误的内容是处理vue文件时出现错误。

怎么解决

作者有百度和微软。 这可能意味着happypack不支持vue-loader。 同时我也根据错误报告检查了解决方案。 通过设置并行参数,仍然无效。

作者甚至怀疑我的happypack配置错误,于是我将原来的配置移植到另一个非vue项目中webpack优化vue,一切正常。 答:这个问题没有答案~

原因分析:

由于vue文件中会含有丰富的CSS,因此vue-loader会提取CSS并将其交给其他加载器进行处理。 vue-loader-plugin 会通过在 vue 文件前面添加查询字符串来告诉其他加载器,以处理该文件。 这是什么意思? 当我们的vue-loader处理该文件时,它会通知其他loader去处理,但是此时loader配置已经被我们重写为happypack,而vue与happypack不兼容,最终导致错误。 很遗憾地告诉大家,vue-cli访问happypack失败。

(注:这部分主要是作者在webpack优化过程中的探索,虽然最终我们不能让自己的webpack包得到很好的优化,但是在探索的过程中,我们也可以学到很多东西~包括vue -cli 配置对象处理?如何调试普通文件nodejs代码?vue-loader中vue文件处理流程?vue-loader-plugin帮我们做什么?而这种东西需要自己慢慢看,踩着坑慢慢理解的~)

优化webpack_webpack优化vue_webpack

2.3 使用dll插件

和大多数webpack优化教程一样,作者也尝试借助dllplugin进行优化。 这个插件的本质就是把我们常用的第三方模块提取出来,单独打包,然后插入到我们的html页面中。 以后我们每次打包的时候,就不需要和第三方模块打交道了。 毕竟第三方模块往往有几万行。

流程介绍:

1.配置webpack.dll.js为第三方库打包

2.在vue.config.js中配置插件

3、将dll封装的js文件引入html中。 (通常采用部署CDN的形式)

由于项目中有很多小的第三方库,比如三、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服务器来返回静态资源)


   

然后npm runserve,开始页面调试开发~

webpack优化vue_webpack_优化webpack

舒服~

优化结果:

由于缺少小型第三方库,时间控制在20s左右。 优化还是比较显着的~

3.优化与探索总结

优化到这里,基本上就结束了。

webpack常见的优化方法有优化路径搜索、设置缓存、happypack、dllplugin等。 前两个vue-cli已经帮我们做了一些,但是happypack因为与vue不兼容而无法访问。 dllplugin 提取第三方库进行了显着优化。 当然,作者也尝试过消除一些项目中无用的代码,但并不痛苦。

webpack优化方法总结:

1.优化模块​​搜索路径

2. 消除不必要和无用的模块

3.设置缓存:缓存加载器的执行结果(cacheDirectory/cache-loader)

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

5.dllplugin提取第三方库

当然这是针对开发的优化,如果是针对部署的优化呢? 我们可以设置splitchunk、按需加载、部署CDN等,这里不再展开。

终于

希望这篇文章能让你有所收获~webpack已经是前端小伙子必备的技能了~如果有时间,可以深入研究一下webpack的配置和原理,你也会有所收获的! 感谢您的观看~