# 十一、打包优化
> 学习打包优化前需要了解 webpack。
所谓的优化主要涉及到两方面:
- 构建速度的优化
- 构建质量的优化
大多数 Vue 项目是基于 VueCLI 搭建的,而 VueCLI 的底层建筑是 webpack。webpack 是现在主流的功能强大的模块化打包工具,在使用 webpack 时,如果不注意性能优化,有非常大的可能会产生性能问题,性能问题主要分为**开发时打包构建速度慢**、**开发调试时的重复性工作**、以及**输出文件质量不高**等,因此性能优化也主要从这些方面来分析。
本文主要针对是在 Vue 项目中关于底层建筑 webpack 优化以及 Vue 本身相关的一些优化事宜。
## 把 VueCLI 升级到最新稳定版
> VueCLI 是基于 webpack 构建的,它的升级可能会带来编译速度的提升或是编译质量的提升。
使用下面的命令查看你的 VueCLI 版本:
```sh
vue --version
```
升级 VueCLI:
```sh
npm install -g @vue/cli
```
升级你的项目中 VueCLI 相关的内容,最好在升级之前确保你项目的 Git 工作区是干净的,所有代码都是已提交到历史记录中了,防止升级带来问题影响,有了问题我们可以及时回退。
```sh
# 在你的项目目录中执行这个命令
vue upgrade
```
## 分析打包结果
> 如果你的编译速度慢,那么可以通过分析打包结果来知道是哪个包比较费时。
- 了解打包的内容
- 找出最大的模块是什么
- 查找错误到达的模块
- 优化它!
通过分析打包结果,我们可以看到哪些包占用了打包的时间,还有哪些包的结果体积比较大。
`vue-cli-service build` 会在 `dist/` 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里。
这里还有一些有用的命令参数:
- `--modern` 使用[现代模式](https://cli.vuejs.org/zh/guide/browser-compatibility.html#现代模式)构建应用,为现代浏览器交付原生支持的 ES2015 代码,并生成一个兼容老浏览器的包用来自动回退。
- `--target` 允许你将项目中的任何组件以一个库或 Web Components 组件的方式进行构建。更多细节请查阅[构建目标](https://cli.vuejs.org/zh/guide/build-targets.html)。
- `--report` 和 `--report-json` 会根据构建统计生成报告,它会帮助你分析包中包含的模块们的大小。
- 内部使用的 [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) 插件。
构建打包并生成分析报告:
```sh
npm run build --report
```
参考:
- https://cli.vuejs.org/zh/guide/cli-service.html#vue-cli-service-build
## Gzip 压缩
> Gzip 压缩是一种数据传输过程中的压缩方式,它可以极大的压缩文件的大小。
>
> 注意:它不影响原始文件。
网站加载的速度很大程序取决于网站资源文件的大小,减少要传输的文件的大小可以使网站不仅加载更快,而且对于那些宽带是按量计费的用户来说也更友好。
HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP[压缩技术](https://baike.baidu.com/item/压缩技术)来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。
Gzip 是什么?
```
一种文件压缩格式。
```
用于哪里?
```
HTTP 协议
```
开启 Gzip 有什么好处?
```
Gzip 开启以后会将输出到用户浏览器的数据进行压缩的处理,这样就会减小通过网络传输的数据量,提高文件传输的速度。
```
> 注意:`gzip`不一定适用于所有文件的压缩。例如,文本文件压缩得非常好,通常会缩小两倍以上。另一方面,诸如JPEG或PNG文件之类的图像已经按其性质进行压缩,使用`gzip`压缩很难有好的压缩效果或者甚至没有效果。压缩文件会占用服务器资源,因此最好只压缩那些压缩效果好的文件。
>
> 资源太小的文件也不会压缩。
如何开启?它需要前后端配置才可以。后端要把服务器软件开启 Gzip 压缩功能(主流的服务器软件都是默认开启 Gzip 压缩的),客户端不需要做任何处理,取决于浏览器(比较新的都支持)。
不同服务器软件配置不一样,具体由部署项目的人负责,一般是运维、后端开发人员,如果想要自行配置,可自行百度查询。大多数服务器软件都是默认开启的。
- Nginx
- Tomcat
- Apache
- IIS
- ...
> 实际工作中,如果服务器软件没有开启,可以和负责运维部署的人员沟通。
如何检测内容是否已开启了 Gzip 压缩?查看响应头中是否有下面的字段信息。
```
Content-Encoding: gzip
```
前端没有调整配置服务器软件麻烦,该怎么测试?
使用 VueCLI 官方推荐的 [serve](https://github.com/zeit/serve) 命令行工具。
```sh
# 1、如果你已经安装了就不需要安装了
npm install --global serve
# 使用该命令检测是否已安装或者是否安装成功,如果能看到输出一个版本号,则证明安装好了
serve --version
# 2、在打包的结果目录中执行下面的命令启动一个 HTTP 静态服务(默认开启 Gzip 压缩启动服务)
serve -s ./
# 使用 -u 参数禁用 Gzip 压缩
serve -s -u ./
```
不是每个浏览器都支持gzip的,如何知道客户端是否支持gzip呢,请求头中有个 `Accept-Encoding` 来标识对压缩的支持。客户端http请求头声明浏览器支持的压缩方式,服务端配置启用压缩,压缩的文件类型,压缩方式。当客户端请求到服务端的时候,服务器解析请求头,如果客户端支持gzip压缩,响应时对请求的资源进行压缩并返回给客户端,浏览器按照自己的方式解析,在http响应头,我们可以看到 `Content-encoding: gzip`,这是指服务端使用了 `gzip` 的压缩方式。
![image-20191207183629949](../../../../../../images/image-20191207183629949.png)
> 在红色矩形区域右键选择:Response Headers > Content-Encoding
![image-20191207183655734](../../../../../../images/image-20191207183655734.png)
> 然后就可以观测到所有请求记录的响应内容编码了。
## 不打包第三方包
> 作用:提高编译的速度。
影响打包速度最根本的原因就是某些第三方包体积过大,所以打包速度就很慢。
解决它的办法也非常简单:不对它打包!不要让 webpack 来处理它。
可是不处理它,把它放哪里呢?通过 `script` 标签加载它,就不被 webpack 处理了。
我们推荐使用第三方的 CDN 来加载资源,所谓的 CDN 说白了就是一个在线链接。
- bootcdn:不错,国内的一个服务
- cdnjs:不推荐,国外的,速度慢
- unpkg:不推荐,国外的,速度慢
- [jsdelivr](https://www.jsdelivr.com/) :国外的,但是在国内有它的服务节点,推荐
- 全球 CDN 优化
- 凡是能通过 npm 下载的包,它里面都有
```html
<!-- element 依赖了 Vue,所以这里也必须加载 Vue -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.13.1/lib/theme-chalk/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/element-ui@2.13.1/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script>
```
但是通过 script 标签加载的资源如何使用呢?那就得做一些特殊配置了。
在项目的根目录创建 `vue.config.js`
```js
/**
* Vue CLI 的配置文件
* 这里可以自定义 VueCLI 内部的 webpack 配置
*/
// 该配置文件必须导出一个对象(Node 中的模块语法)
module.exports = {
// 自定义 VueCLI 中的 webpack 配置
configureWebpack: {
// 告诉 webpack 使用 script 标签加载的那个资源,而不是去 node_moudles 中打包处理
externals: {
// 属性名:你加载的那个包名
// 属性值:script 标签暴露的全局变量,注意,写到字符串中!!!
// 'element-ui': 'ELEMENT'
'vue': 'Vue',
'element-ui': 'ELEMENT',
'echarts': 'echarts'
}
}
}
```
重新打包测试。
> 使用建议:只需要把一些特别大的包处理即可,超过500kb 的包就做这个处理。
## 路由懒加载
> 参考文档:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
路由懒加载主要针对的是我们自己的代码。
默认情况下,我们自己写的所有代码都会被打包到 app.xxx.js 中。
如果我们自己的代码非常多,将来 app.xxx.js 也会随之变得越来越大。文件太大的话会造成**首次加载很慢**。
所以我们可以通过路由懒加载的方式优化它。
## 按需加载第三方组件
项目中使用的第三方组件库,如果使用的组件不是太多,建议按需加载,所谓的按需加载就是仅仅只把使用到的组件打包到结果中,可以减小打包结果体积。
## 缓存和并行处理
VueCLI 内置了:
- `cache-loader` 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 `node_modules/.cache` 中——如果你遇到了编译方面的问题,记得先删掉缓存目录之后再试试看。
- `thread-loader` 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启
如果你没有使用 VueCLI,自己搭建的 webpak,建议加上这两个工具优化构建速度。
你可能会看到一些几年前的“优化教程”,它们可能会涉及到这个内容。