1 首屏加载速度优化
优化首屏加载速度的方法有很多,比如说之前提到的打包体积的优化。本文在优化打包体积的基础上,使用预渲染进一步优化首屏加载速度。
在实际操作中发现,CDN引入与预渲染在vue.config.js同时配置会冲突。打包部署后的应用会出现页面路由不会跳转的问题,即无论如何点击和切换路由,页面都不会动(有了解的朋友可以来解惑一波)。所以本次对首屏加载速度的优化就仅采用 路由懒加载+预渲染+gzip打包 的方式进行优化。这一优化过程需要服务端进行相应的配合。
预渲染为什么会减少首屏加载的时间
默认情况下,Vue生成的应用是单页面应用,打包后的项目只有一个HTML页面。Vue单页面应用的好处是在刷新/切换页面的时候,只切换相应的组件,减少了切换时的开销。但是随之而来的问题就是首屏加载时间的增加,这是因为我们看到的HTML页面都是JS拼起来的,而浏览器执行JS来生成页面需要消耗较长的时间。
预渲染则是在打包的时候就针对部分路由直接生成HTML文件,这样不需要在浏览器内再进行拼接,减少了时间的消耗。不难发现,这个思想其实结合了单页面应用以及多页面应用的优点,对于一些重要的页面,使用打包后的现成的html页面,可以大大提高首屏加载速度。
2 预渲染
与SEO 优化——预渲染(prerender)中的步骤类似,只是这次的场景换到了Vue3,选用prerender-spa-plugin-next包来实现预渲染。
- npm i prerender-spa-plugin-next
- 在vue.config.js里配置
const plugins = [];
if (process.env.NODE_ENV === "production") {
const { join } = require("path");
const PrerenderPlugin = require("prerender-spa-plugin-next");
plugins.unshift(
new PrerenderPlugin({
staticDir: join(__dirname, "dist"),
routes: ["/", "/robot"], //the page route you want to prerender
renderAfterDocumentEvent: "render-event",
})
);
}
不需要再main.js里配置
3 gzip
plugins: [
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), // 匹配文件名
threshold: 10, // 对超过10K的数据进行压缩
minRatio: 0.8, //极小比
deleteOriginalAssets: true,
}),
]
4 路由懒加载
对于上文中配置了需要预渲染的页面,不使用懒加载;对于未配置预渲染的页面,使用懒加载。 使用懒加载后,打包的CSS和JS文件数量会增加,单个文件体积会减小,这样进入不同页面的时候,只需要下载相应的CSS以及JS文件即可,不需要下载一个很大的CSS以及JS文件。
5 CDN引入
CDN引入通常来说是减小打包体积的不错的方法,但是如果我们使用的CDN是从jsdelivr或者bootcdn中引入的,就会存在CDN服务器挂了造成的项目无法正常运行的情况。通常的解决方法是:
- 放弃CDN引入
- 监听JS错误事件,在监听到错误时立即更换CDN源
- 将CDN资源部署在自己的服务器上
6 后端——flask
由于打包后的文件为.gz格式,因此以flask为例进行后端的适配
@app.route('/<path:path>')
def history(path):
if path.startswith('css/') or path.startswith('js/')\
or path.startswith('img/') or path == 'favicon.ico':
if(os.path.exists('dist/'+path)):
return app.send_static_file(path)
else:
return app.send_static_file(path+'.gz'),200,[("Content-encoding","gzip")]
else:
return app.send_static_file('index.html')
与搭配history路由的写法可以结合起来,Flask基础(前端视角),由于之前在打包成.gz文件,但是前端请求的文件不带.gz,因此首先去寻找是否可以找到非压缩的文件,找不到就选择返回压缩的文件,并返回Content-encoding:gzip。
7 刷新页面时,会先闪一下index页面再进入目标的页面
这是由于后端配置history路由的时候,对没有配置过的路由,统一跳转到dist/index.html。但是我们配置过预渲染以后,实际上要跳转到时dist/robot/index.html。因此,对/robot进行配置
@app.route('/robot')
def robot():
return app.send_static_file('robot/index.html')
8 结果
- 懒加载+CDN+gzip
- 懒加载+预渲染+gzip
可以发现,使用预渲染的效果会更好。一方面由于两个方案都有gzip的引入,体积大小对于首屏加载速度 的影响已经差别不大了,又因为预渲染是在打包的时候就生成HTML文件,而不是使用js在客户端进行渲染,速度会快很多。
补充:lighthouse里面的几个参数的意义:
FCP(First Contentful Paint)首次内容渲染时间
LCP(Largest Contentful Paint) 最大内容渲染时间
Time to Interactive 可交互时间
Total Blocking Time 累积阻塞时长
Cumulative Layout Shift 累积布局偏移
Speed Index 速度指数,可以理解为所有元素加载的平均时间
这些参数都越小越好