深入理解基于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 form 验证函数 当前比较流行的错误提示
Jun 23 Javascript
服务器端的JavaScript脚本 Node.js 使用入门
Mar 07 Javascript
jquery简单图片切换显示效果实现方法
Jan 14 Javascript
jquery弹出遮掩层效果【附实例代码】
Apr 28 Javascript
常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全(推荐)
Dec 20 Javascript
简单实现IONIC购物车功能
Jan 10 Javascript
VUE中的无限循环代码解析
Sep 22 Javascript
pace.js和NProgress.js两个加载进度插件的一点小总结
Jan 31 Javascript
使用ngrok+express解决本地环境中微信接口调试问题
Feb 26 Javascript
Vue实现导出excel表格功能
Mar 30 Javascript
webpack打包html里面img后src为“[object Module]”问题
Dec 22 Javascript
javascript使用Blob对象实现的下载文件操作示例
Apr 18 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
Yii2.0多文件上传实例说明
2017/07/24 PHP
Thinkphp5框架使用validate实现验证功能的方法
2019/08/27 PHP
Document:getElementsByName()使用方法及示例
2013/10/28 Javascript
JavaScript中setInterval的用法总结
2013/11/20 Javascript
用正则表达式替换图片地址img标签
2013/11/22 Javascript
jQuery中attr()和prop()在修改checked属性时的区别
2014/07/18 Javascript
jQuery插件ajaxFileUpload实现异步上传文件效果
2015/04/14 Javascript
chrome不支持form.submit的解决方案
2015/04/28 Javascript
jQuery EasyUI之DataGrid使用实例详解
2016/01/04 Javascript
javascript每日必学之封装
2016/02/23 Javascript
js实现表格筛选功能
2017/01/18 Javascript
bootstrap输入框组件使用方法详解
2017/01/19 Javascript
js实现带简单弹性运动的导航条
2017/02/22 Javascript
AngularJS实现tab选项卡的方法详解
2017/07/05 Javascript
webpack教程之webpack.config.js配置文件
2017/07/05 Javascript
jQuery实现的隔行变色功能【案例】
2019/02/18 jQuery
Vue表单绑定的实例代码(单选按钮,选择框(单选时,多选时,用 v-for 渲染的动态选项)
2019/05/13 Javascript
tsconfig.json配置详解
2019/05/17 Javascript
vue实现多级菜单效果
2019/10/19 Javascript
JS模拟实现京东快递单号查询
2020/11/30 Javascript
Vue实现多页签组件
2021/01/14 Vue.js
[01:11:02]Secret vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
python批量生成本地ip地址的方法
2015/03/23 Python
Python扩展内置类型详解
2018/03/26 Python
详解Python如何生成词云的方法
2018/06/01 Python
Python Matplotlib库安装与基本作图示例
2019/01/09 Python
Python qqbot 实现qq机器人的示例代码
2019/07/11 Python
CSS3实现文字波浪线效果示例代码
2016/11/20 HTML / CSS
html5 viewport使用方法示例详解
2013/12/02 HTML / CSS
HTML5拍照和摄像机功能实战详解
2019/01/24 HTML / CSS
国家励志奖学金个人先进事迹材料
2014/05/04 职场文书
蛋糕店创业计划书范文
2014/09/21 职场文书
商标侵权律师函
2015/05/27 职场文书
CSS3实现的侧滑菜单
2021/04/27 HTML / CSS
html+css实现赛博朋克风格按钮
2021/05/26 HTML / CSS
抖音动画片,皮皮虾,《治愈系》动画在用这首REMIX作为背景音乐,Anak ,The last world with you完整版
2022/03/16 杂记