最近这几个月一直在开发一个使用 vue-cli
搭建起来的项目。最近打包上线之后。发现首屏打开特别慢,在网络好的情况下大约需要4s 至 5 s。 在网络不好的情况下,还需要7s 8 s。加载的期间一直显示白屏,导致用户的体验非常不好。所以针对这个问题来做一些优化;期望的结果是首屏加载得快一点。白屏缩短。
存在的问题
首先我们来看没有优化之前的耗时时间:
在网络比较好的情况下, 耗时:6.86s。 平均是 7s 左右。
什么是首屏加载
首屏时间(First Contentfull Paint): 指的是响应用户在浏览器上输入URL
网址, 到首屏内容渲染完后才能的一个时间。此时整个网页不一定要渲染完成,但旭要展示当前视窗需要的内容。
首屏加载时间的计算
可以通过DOMContentLoad
或者是 performance
来计算首屏时间
1 | windown.addEventListner("DOMContentLoad", (event) => { |
1 | performance.getEntriesByName("first-contentfull-paint")[0].startTime; |
加载慢的原因
在页面渲染的过程,导致加载速度慢的因素可能如下:
网络请求慢
资源体积过大
重复请求资源
加载脚本堵塞了渲染
解决的办法
我们知道 vue
, react
等框架都是js
渲染的html
。是典型的单页应用,首次加载耗时多,因此优化Vue
项目首屏加载对于提升用户体验非常重要。所以必须要等到这个js
文件加载完成后界面才会显示。
路由懒加载
vue-router
实现路由懒加载的方法有哪些?首先我们先来看vue-router
的一些官方解释
什么是路由懒加载?
也叫延迟加载,即在需要的时候进行加载,随用随载。
为什么需要懒加载呢?
- 首先,我们知道路由中通常会定义很多不同的页面。
- 这个页面这项目build打包后,一般情况下,会放在一个单独的js文件中
- 但是,如果很多的页面都放在同一个js文件中,必然会造成这个页面非常大
- 如果我们一次性的从服务器中请求下来这个页面,可能会花费一定时间,用户体验不好
在我们的vue 项目中:
像vue这种单页面应用,如果没有应用懒加载,运用
webpack
打包后的文件将会异常的大。造成进入首页时,需要加载的内容过多,时间过长,会出啊先长时间的白屏,即使做了
loading
也是不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
总的来说:进入页面不用也不需要一次性加载过多资源造成加载时间过程!
路由懒加载的原理
主要作用是将路由对应的组件打包成一个个的js代码块
只有在这个路由被访问到的时候,才加载对应的组件,否则不加载!
总结的来说:只有在这个路由被访问到的时候,才加载对应的组件,否则不加载!
路由懒加载,在访问到当前页面才会加载相关的资源,异步方式分模块加载文件
如何实现路由懒加载?
Vue
异步组件ES6
标准语法import()
———推荐使用webpac
k的require
,ensure()
我们使用ES6
标准语法import()
来实现懒加载
没有用到路由加载懒加载之前是这么写的
1 | import page404 from "@/views/error/page404.vue"; |
使用路由懒加载:
1 | const page404 = () => import("@/views/error/page404.vue"); |
压缩图片
我们先看打包之后下面的img 的图片的大小:
总的5.6M。 有点大
我们使用 vue inspect > output.js
导出 vue-cli
做的的默认webpoack
配置:
发现对于图片, 只用到了 url-loader 。 相对一些比较大的图片。是可以进行压缩的。可以使用 image-webpack-loader
我们在 vue-config.js
配置:
1 | const chainWebpack = function chainWebpacks (config) { |
然后再进行npm run build
打包在看看img
:
图片的大小从 5.8M 变到了 1.8M。 感觉效果还是明显的。
经过上面这两步优化,我们再看来首屏加载的时间:
首页加载的数据的耗时明显减少了大概 1/2 时间, 棒棒哒
gzip 压缩
从上面我们可以看出:vendor-chunks.js
很大。当我们的项部署了之后, 我们的资源文件请求会保持原来的大小。如果文件过大,并且很多的情况下,会导致网络请求耗时。严重点可能会阻塞后面的进程。针对这样的情况, 我们有没有什么比较好的解决方法呢? 有的, 那就进行 gzip
压缩。
gzip
压缩有两种方式:
- 服务器压缩文件
- 前端 webpack 打包生成 gz 文件
那我们先来看看这两种方式:
服务器压缩文件
这种方式是浏览器请求文件时,服务器对该文件进行压缩后传输给浏览器。前端不用做任何的配置,不需要 webpack
生成 .gz
文件。而是服务器自己处理。就拿 Nginx 来举例,我么打开 nginx.conf 文件, 会有默认配置,默认的 #gzip on;
即不打开。
nginx 文件结构
1 | ... # 全局块 |
在 http 块这里开启 gzip
和相关的配置:
1 | http { |
这种方案的特点:使用nginx
在线gzip
,缺点就是耗性能,需要实时压缩,但是vue
打包后的文件体积小。
前端 webpack 打包生成 gz 文件
这次优化主要是采用这种方式。
这种方式是打包的时候通过 webpack
配置生成对应的.gz
文件,浏览器请求文件时,服务器返回相应的的文件的 .gz
文件。
安装 compression-webpack-plugin
1 | npm i compression-webpack-plugin -D |
然后再vue.config.js
中设置
1 | const CompressionPlugin = require("compression-webpack-plugin"); |
启用gzip压缩打包之后,会变成下面这样,自动生成gz
包。目前大部分主流浏览器客户端都是支持gzip的,就算小部分非主流浏览器不支持也不用担心,不支持gzip格式文件的会默认访问源文件的,所以不要配置清除源文件。所以这时候打包的总体积会变大, 是因为我们没有删除源文件。是为了防止有些浏览器不支持的时候能返回源文件。
上面test
匹配的压缩文件类型, 并没有对图片进行压缩,因为图片压缩并不能实际减少文件大小,反而会导致打包后生成很多同大小的gz文件,得不偿失。
这种方式是浏览器在请求资源时,服务器返回相应的 .gz
文件。 所以需要在服务器配置一个属性, 期望它能够正常返回我们需要的.gz
文件
ginx
举例(nginx.conf
文件):
1 | http { |
其中gzip_static on
这个属性是静态加载本地的gz文件
我们先来看采用这种方法前的请求的chunk-vendors.js
的大小:
我们可以看到请求的这个文件大小有 5.4 MB。
我们采用gzip
压缩之后,请求该文件的大小:
可以看出来,请求文件的大小从 5.4MB 变成了 854KB。 而首页的加载时间较少的幅度不是很大, 但也是减少了。
nginx配置了静态gz加载后,浏览器也返回的是gz文件,这样就会请求小文件而不会导致请求卡线程,并且,因为保留了源文件,所以当我们删除gz后,浏览器会自动去请求原始文件,而不会导致界面出现任何问题
静态加载gz文件主要是依托于下面的请求头:
这种优化的主要特点: webpack
打包,然后直接使用静态的gz
,缺点就是打包后文件体积太大,但是不耗服务器性能。
Webpack打包体积优化
从图上我们发现首屏加载过程中总公发起了149次请求。这显然是不友好的。那我们可以考虑考虑减少首屏加载的请求次数。
减少首屏加载请求次数可以从下面这个方面入手:
- 使用
splitChunks
分离代码并实现相关模块共享,最终的目的就是减少请求资源的大小和请求次数
splitChunks
SplitChunks插件是什么呢,简单的来说就是Webpack中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。 在Webpack出现前,提取公共代码是人为去处理,而SplitChunks插件的作用就是通过配置让Webpack去帮你提取公共代码
用SplitChunks插件来控制Webpack打包生成的js文件的内容的精髓就在于,防止模块被重复打包,拆分过大的js文件,合并零散的js文件。 最终的目的就是减少请求资源的大小和请求次数
我们先来看splitChunks
的一下字段说明吧
1 | const path = require("path"); |
splitChunks优化
使用webpack-bundle-analyzer 进行体积分析。该插件可生成依赖包形成可视化分析图谱,帮组开发者分析项目结构
安装:
1 | npm install --save-dev webpack-bundle-analyzer |
将插件添加到webpack
中,因为使用的是vue-cli,所以应在vue.config.js
中添加配置:
1 | const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; |
运行npm run build
生成分析页面
从分析页面可以看出 chunk-vendor
很大。因为这个chunk
是项目所有的依赖库,从它是打包了node_modules
可以看出,所以很影响性能。从图中发现monaco-editor
在线编辑器占了很大体积。 我们可以把它抽离出来。因为用的地方太多了, 导致很难做成按需加载了的了。
在vue.config.js
配置:
1 | config.optimization.splitChunks({ |
我们再来看下分析图:
可以看出monaco-editor
和echarts
被单独打包了。
现在我们来看最终的结果:
请求次数从 149 减到了 115 次。可观的是首页加载耗时减到了 1.74s。 平均值大概 1.5 s。 表示还是可观的。
注意: SplitChunks
插件对代码作更细致的拆分 需要注意的减少请求数必然使得单个文件体积变大,二者是矛盾的,最佳实践是取得一个中庸的值平衡优劣.
时间轴回放
优化方法 | 请求数量 | 耗时时间 |
---|---|---|
未做任何优化 | 149次 | 6.86s |
路由懒加载+压缩图片 | 149次 | 4.26s |
splitChunks分离代码 | 115次 | 1.74s |