深入理解基于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 相关文章推荐
小议javascript 设计模式 推荐
Oct 28 Javascript
海量经典的jQuery插件集合
Jan 12 Javascript
原创javascript小游戏实现代码
Aug 19 Javascript
AngularJs动态加载模块和依赖注入详解
Jan 11 Javascript
jQuery如何解决IE输入框不能输入的问题
Oct 08 Javascript
原生JS与jQuery编写简单选项卡
Oct 30 jQuery
vue实现pdf导出解决生成canvas模糊等问题(推荐)
Oct 18 Javascript
js实现导航跟随效果
Nov 17 Javascript
vue + typescript + video.js实现 流媒体播放 视频监控功能
Jul 07 Javascript
vue实现行列转换的一种方法
Aug 06 Javascript
Vue混入mixins滚动触底的方法
Nov 22 Javascript
通过js随机函数Math.random实现乱序
May 19 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设计模式 State (状态模式)
2011/06/26 PHP
PHP生成腾讯云COS接口需要的请求签名
2018/05/20 PHP
js获取当前select 元素值的代码
2010/04/19 Javascript
IE8下关于querySelectorAll()的问题
2010/05/13 Javascript
JQuery UI DatePicker中z-index默认为1的解决办法
2010/09/28 Javascript
js 日期比较相关天数代码
2014/04/02 Javascript
jQuery实现按钮的点击 全选/反选 单选框/复选框 文本框 表单验证
2015/06/25 Javascript
深入理解$.each和$(selector).each
2016/05/15 Javascript
AngularJS过滤器filter用法分析
2016/12/11 Javascript
Javascript this 函数深入详解
2016/12/13 Javascript
轻松理解Javascript变量的相关问题
2017/01/20 Javascript
javascript事件的绑定基础实例讲解(34)
2017/02/14 Javascript
Vue项目中设置背景图片方法
2018/02/21 Javascript
js提取中文拼音首字母的封装工具类
2018/03/12 Javascript
vue3.0 CLI - 2.4 - 新组件 Forms.vue 中学习表单
2018/09/14 Javascript
[55:16]Mski vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
Python中的多行注释文档编写风格汇总
2016/06/16 Python
Python去除、替换字符串空格的处理方法
2018/04/01 Python
Python使用sklearn库实现的各种分类算法简单应用小结
2019/07/04 Python
基于Python采集爬取微信公众号历史数据
2020/11/27 Python
vue.js刷新当前页面的实例讲解
2020/12/29 Python
巴西食品补充剂在线零售商:Músculos na Web
2017/08/07 全球购物
印度民族服装购物网站:BIBA
2019/08/05 全球购物
畜牧兽医本科生个人的自我评价
2013/10/11 职场文书
《小小竹排画中游》教学反思
2014/02/26 职场文书
教师教学评估方案
2014/05/09 职场文书
群众路线教育实践活动民主生活会个人检查对照思想汇报
2014/10/04 职场文书
环境卫生工作汇报材料
2014/10/28 职场文书
股权转让协议书
2014/12/07 职场文书
2015关爱留守儿童工作总结
2014/12/12 职场文书
房产公证书格式
2015/01/26 职场文书
工作迟到检讨书范文
2015/05/06 职场文书
会计试用期工作总结2015
2015/05/28 职场文书
2016年春季运动会广播稿
2015/08/19 职场文书
区域销售大会开幕词
2016/03/04 职场文书
background-position百分比原理详解
2021/05/08 HTML / CSS