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


Posted in Javascript onOctober 14, 2019
转眼已经是2019年,短短三四年时间,webpack打包工具成为了前端开发中必备工具,曾经一度的面试题都是问,请问前端页面优化的方式有哪些?大家也是能够信手拈来的说出缓存、压缩文件、CSS雪碧图以及部署CDN等等各种方法,但是今天不一样了,可能你去面试问的就是,请问你是否知道webpack的打包原理,webpack的打包优化方法有哪些?所以该说不说的,笔者闲着没事研究了一下webpack的打包优化,可能大家都有看过类似的优化文章~ 但是笔者还是希望能够给大家一些新的启发~

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脚手架的,所以其实vue-cli中已经帮你做了一些优化的工作,可以看到,原先项目最初的配置设置了splitchunk,进行代码分割,这在大型项目中是很有必要的,毕竟你不希望你的用户阻塞加载一个5MB大小的JS文件,所以做代码分割和懒加载是很有必要的。

说远了,我们来看看这个配置,你需要用smp对配置进行再包裹,因为SpeedMeasurePlugin会对你的其他Plugin对象包裹一层代理,这样的目的是为了能够知道plugin开始和结束的时间~

其次,BundleAnalyzerPlugin就跟普通的plugin一样,加载plugins数组的后面即可。

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

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

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

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

2、开始逐步优化

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

这是webpack优化中的常规操作,基本就是对模块和文件查找的优化,以及减少loader对一些不必要模块的处理,但是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)$/
},
  • 通过modules指定查找第三方模块的路径。
  • 通过alias指定第三方模块直接查找到打包构建好的压缩js文件。
  • 通过module指定noparse,对第三方模块不再进行分析依赖。

优化效果:2s?

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

可以看到时间就减少了两三秒,在30s波动,感觉没有多大差别。

2.2尝试使用happypack

由于在进行webpack优化前,翻阅了很多有关webapck优化的文章,所以笔者也想尝试一下用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的源码进行调试,可看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的webpack打包优化实践及探索

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

通过这段代码我们可以看出,如果我们configWepack配置为函数,之后通过参数的形式获取到config配置项,本身是一个对象,对象是保留引用的形式,所以如果我们直接对传入的config对象进行修改,就可以实现我们最初的目标!修改vue-cli内置的loader!

当然,除了断点进入里面看配置,刚才也说了,我们可以通过命令行输出为一个output文件查看现有的配置。

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

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

可能有点废话了,但是通过断点的方式,我们可以看到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了,但是很遗憾的告诉大家,报错了...

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

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

如何解决

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

笔者甚至怀疑是自己的happypack配置不对,于是我把配置原样移植配置到另一个非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帮我们做了什么事?而这些都是要自己慢慢翻阅,慢慢踩坑去了解的~)

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,开始页面调试和开发~

舒服~

优化结果:

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

由于少了大型第三方库,所以时间控制在了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的配置和原理,也是会有所收获的!谢谢观看~

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

Javascript 相关文章推荐
js固定DIV高度,超出部分自动添加滚动条的简单方法
Jul 10 Javascript
JS实现兼容性较好的随屏滚动效果
Nov 09 Javascript
基于JS代码实现实时显示系统时间
Jun 16 Javascript
jQuery插件cxSelect多级联动下拉菜单实例解析
Jun 24 Javascript
用jQuery向div中添加Html文本内容的简单实现
Jul 13 Javascript
基于SpringMVC+Bootstrap+DataTables实现表格服务端分页、模糊查询
Oct 30 Javascript
VueJS全面解析
Nov 10 Javascript
es6学习笔记之Async函数基本教程
May 11 Javascript
jQuery ajax动态生成table功能示例
Jun 14 jQuery
浅谈在vue项目中如何定义全局变量和全局函数
Oct 24 Javascript
实现高性能javascript的注意事项
May 27 Javascript
vue组件库的在线主题编辑器的实现思路
Apr 03 Javascript
Vue3.0 响应式系统源码逐行分析讲解
Oct 14 #Javascript
微信小程序 textarea 层级过高问题简单解决方案
Oct 14 #Javascript
vue的路由映射问题及解决方案
Oct 14 #Javascript
浅谈Vue为什么不能检测数组变动
Oct 14 #Javascript
为什么Vue3.0使用Proxy实现数据监听(defineProperty表示不背这个锅)
Oct 14 #Javascript
Vue3.0中的monorepo管理模式的实现
Oct 14 #Javascript
Vue3 源码导读(推荐)
Oct 14 #Javascript
You might like
PHP资源管理框架Assetic简介
2014/06/12 PHP
PHP常量define和const的区别详解
2019/05/18 PHP
Yii框架的布局文件实例分析
2019/09/04 PHP
javascript中巧用“闭包”实现程序的暂停执行功能
2007/04/04 Javascript
实用框架(iframe)操作代码
2014/10/23 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
Angularjs中UI Router全攻略
2016/01/29 Javascript
jQuery Checkbox 全选 反选的简单实例
2016/11/29 Javascript
微信小程序 跳转传参数与传对象详解及实例代码
2017/03/14 Javascript
vue 设置proxyTable参数进行代理跨域
2018/04/09 Javascript
React实现全局组件的Toast轻提示效果
2018/09/21 Javascript
在nodejs中创建child process的方法
2021/01/26 NodeJs
[01:06:54]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第一场 1月24日
2021/03/11 DOTA
Python2.x利用commands模块执行Linux shell命令
2016/03/11 Python
Python实现ssh批量登录并执行命令
2016/10/25 Python
Django组件content-type使用方法详解
2019/07/19 Python
如何将你的应用迁移到Python3的三个步骤
2019/12/22 Python
用python按照图像灰度值统计并筛选图片的操作(PIL,shutil,os)
2020/06/04 Python
解决keras GAN训练是loss不发生变化,accuracy一直为0.5的问题
2020/07/02 Python
python else语句在循环中的运用详解
2020/07/06 Python
Python自动巡检H3C交换机实现过程解析
2020/08/14 Python
纯css3(无图片/js)制作的几个社交媒体网站的图标
2013/03/21 HTML / CSS
Ray-Ban雷朋美国官网:全球领先的太阳眼镜品牌
2016/07/20 全球购物
JD Sports比利时官网:英国领先的运动鞋和运动服饰零售商
2018/10/10 全球购物
Hush Puppies澳大利亚官网:舒适的男女休闲和正装鞋
2019/08/24 全球购物
毕业生文员求职信
2013/11/03 职场文书
开学寄语大全
2014/04/08 职场文书
支行行长竞聘演讲稿
2014/05/15 职场文书
“四风”问题对照检查材料思想汇报
2014/09/16 职场文书
个人查摆剖析材料
2014/10/04 职场文书
晋江市委常委班子四风问题整改工作方案
2014/10/26 职场文书
行政二审代理词
2015/05/25 职场文书
新店开业策划方案怎么书写?
2019/07/05 职场文书
如何使JavaScript休眠或等待
2021/04/27 Javascript
HTML通过表单实现酒店筛选功能
2021/05/18 HTML / CSS
vue+springboot实现登录验证码
2021/05/27 Vue.js