减小项目打包体积、提升打包速度,是前端性能优化中非常重要的环节,笔者结合工作中的实践总结,梳理出一些 。
项目背景
技术栈:vue-cli3 + vue2 + webpack4
主要插件:elementUI + echarts + axios + momentjs
目标: 通过一系列的优化方案,对比打包体积和速度的前后变化,来验证方案的有效性
项目打包指标
1. 生成项目打包报告
可以通过添加–report命令:
1 | // 在使用 build 命令的时候加上一个 --report 参数即可 |
有时可能会失效,可尝试使用以下命令:
1 | npm run build -- --report |
打包后 dist 目录会生成 report.html 文件,打开这个这个文件就可以看到每个模块的体积大小。
或者通过安装 webpack-bundle-analyzer 插件来分析,步骤如下:
1)安装
1 | npm install webpack-bundle-analyzer -D |
2)vue.config.js中 引入
1 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; |
3)npm run serve运行后,在浏览器打开http://127.0.0.1:8888/
可以看到分析页面
2. 查看初始体积和打包速度
● 初始体积 2.25M
● 初始打包速度 25386ms
开始优化 ✈︎
1、externals 提取项目依赖
从上面的打包分析页面中可以看到,chunk-vendors.js 体积为 2.21M,其中最大的几个文件都是一些公共依赖包,那么只要把这些依赖提取出来,就可以解决 chunk-vendors.js 过大的问题
可以使用 externals 来提取这些依赖包,告诉 webpack 这些依赖是外部环境提供的,在打包时可以忽略它们,就不会再打到 chunk-vendors.js 中。
1)配置项目打包入口文件
为什么要配置多个项目入口文件呢?
项目在开发阶段为了方便我们使用的第三方库是存在本地的,按需导入的,所以这个时候项目的体积是非常的大的,而项目在发布阶段是通过启用第三方库的CDN进行加载的,所以这个时候我们就需要两个入口文件,一个是开发阶段使用一个是项目打包发布阶段使用
在启用CDN之前需要从单个打包入口文件配置成多个打包入口文件,项目开发阶段使用dev-mian.js文件,项目发布阶段使用prod-mian.js文件
● 将 src 下的 main.js 文件复制一份,重命名为 prod-main.js 而源文件重命名为 dev-main.js
作用:
prod是production的缩写,表示生产的意思,我们需要项目在打包构建的时候使用这个 prod-main.js 的入口文件
dev是development的缩写,表示开发的意思,在项目开发阶段,我们使用 dev-main.js 这个入口文件
这两步完成之后,可以在 prod-main.js 这个文件中将我们开发阶段引入的一些第三方的库和js文件注释掉,采用 CDN 的方式直接在window全局查找这些第三方资源,能够有效的提升打包的速度和减少项目的体积大小
● 复制完成之后将开始配置这两个入口文件,在项目根目录新建一个叫 vue.config.js 的文件,这个文件中可以添加一些项目相关的配置
怎么配置打包入口文件?
1 | //向外共享一个对象 |
2)vue.config.js 中配置:
1 | module.exports = { |
3)在 index.html 中使用 CDN 引入依赖
1 | <body> |
验证 externals 的有效性,重新打包,最新数据如下:
打包体积:1.12M:
打包速度:18879ms:
使用 externals 后,包体积压缩50%、打包速度提升26%
2、组件库的按需引入
为什么没有使用 externals 的方式处理组件库呢?
externals缺点:直接在html内引入的,失去了按需引入的功能,只能引入组件库完整的js和css
组件库按需引入的原理:最终只引入指定组件和对应的样式
elementUI 需要借助 babel-plugin-component 插件实现,插件的作用如下:
如按需引入 Button 组件:
1 | import { Button } from'element-ui' |
编译后的文件(自动引入 button.css):
1 | import _Button from"element-ui/lib/button"; |
通过该插件,最终只引入指定组件和样式,来实现减少组件库体积大小
1)安装 babel-plugin-component
1 | npm install babel-plugin-component -D |
2)babel.config.js中引入
1 | module.exports = { |
验证组件库按需引入的有效性,重新打包,最新数据如下:
打包体积:648KB:
打包速度:15135ms:
组件库按需引入后,包体积压缩72%、打包速度提升40%
同时 chunk-vendors.css 的体积也有了明显的减少,从206KB降到了82KB
原始体积:
按需引入后:
3、减小三方依赖的体积
继续分析打包文件,项目中使用了 momentjs,发现打包后有很多没有用到的语言包
使用 moment-locales-webpack-plugin 插件,剔除掉无用的语言包
1)安装
1 | npm install moment-locales-webpack-plugin -D |
2)vue.config.js 中引入
1 | const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); |
验证插件的有效性,重新打包,最新数据如下:
打包体积:407KB:
打包速度:10505ms:
减小三方依赖体积后,包体积压缩82%、打包速度提升59%
4、HappyPack 多线程打包
由于运行在 Node.js 之上的 webpack 是单线程模型的,我们需要 webpack 能同一时间处理多个任务,发挥多核 CPU 电脑的威力
HappyPack 就能实现多线程打包,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程,来提升打包速度
1)安装
1 | npm install HappyPack -D |
2)vue.config.js 中引入
1 | const HappyPack = require('happypack'); |
验证 HappyPack 的有效性,重新打包,最新数据如下:
打包速度:8949ms:
使用HappyPack后,打包速度进一步提升了65%
由于测试项目较小,打包时间缩短的不算太多。实测发现越是复杂的项目,HappyPack 对打包速度的提升越明显
5、Gzip压缩
线上的项目,一般都会结合构建工具 webpack 插件或服务端配置 nginx,来实现 http 传输的 gzip 压缩,目的就是把服务端响应文件的体积尽量减小,优化返回速度。
html、js、css资源,使用 gzip 后通常可以将体积压缩70%以上
这里介绍下使用 webpack 进行 gzip 压缩的方式,使用 compression-webpack-plugin 插件
1)安装
1 | npm install compression-webpack-plugin -D |
2)vue.config.js 中引入
1 | const CompressionPlugin = require('compression-webpack-plugin'); |
验证插件的有效性:
重新打包,原来 407KB 的体积压缩为 108KB
6、DllPlugin 动态链接库
DllPlugin 与 externals 的作用相似,都是将依赖抽离出去,节约打包时间。区别是 DllPlugin 是将依赖单独打包,这样以后每次只构建业务代码,而 externals 是将依赖转化为 CDN 的方式引入
当公司没有很好的 CDN 资源或不支持 CDN 时,就可以考虑使用 DllPlugin ,替换掉 externals
DllPlugin 配置流程大致分为三步:
1)创建 dll.config.js 配置文件
1 | import { DllPlugin } from"webpack"; |
2)package.json 配置脚本
1 | "build:dll": "webpack --config ./dll.config.js", |
3)使用 DllReferencePlugin 将打包生成的dll文件,引用到需要的预编译的依赖上来,并通过 html-webpack-tags-plugin 在打包时自动插入dll文件
vue.config.js 配置如下
1 | import { DllReferencePlugin } from"webpack"; |
先运行 npm run build:dll 打包生成依赖文件,以后只用运行 npm run build 构建业务代码即可
优化总结
经过上面的一系列优化,可以看到:
● 包体积由原来的 2.25M 减少到 407KB,压缩了82%
● 打包速度由原来的 25386ms减少到 8949ms,提升了65%
这些方式虽然很常规,但确实可以有效地提升项目的性能