SPA 首屏加载速度慢如何解决
1. 什么是首屏加载
首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容。
首屏加载可以说是用户体验中最重要的环节。
2. 如何计算出首屏时间
// 方案一
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting')
})
// 方案二
performance.getEntriesByName('first-contentful-paint')[0].startTime
// performance.getEntriesByName('first-contentful-paint')[0]
// 会返回一个 PerformancePaintTiming 的实例,结构如下:
{
name: 'first-contentful-paint',
entryType: 'paint',
startTime: 507.8,
duration: 0
}
3. 产生原因
- 文件体积过大(网络传输慢)。
- 加载脚本时渲染堵塞(JS 执行阻塞主线程)。
- 网络延时。
- 资源重复请求。
4. 解决方案
1) 减少入口文件积
路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包,使得入口文件变小。
方法:在 vue-router 配置路由时,采用动态加载路由的方式:
routes: [
{
path: 'Blogs',
name: 'ShowBlogs',
component: () => import('./components/ShowBlogs.vue')
}
]
2) UI 框架按需加载
日常使用的 UI 框架如 element-ui 或者 antd,按需引入:
import { Button, Input, Pagination, Table, TableColumn, MessageBox } from 'element-ui'
Vue.use(Button)
Vue.use(Input)
Vue.use(Pagination)
3) 静态资源本地缓存
合理使用 localStorage、sessionStorage、HTTP 缓存(Cache-Control / ETag)、Service Worker。
4) 重复组件进行打包
假设 A.js 文件是一个常用的库,现在有多个路由使用了 A.js 文件,这就造成了重复下载。
解决方案:在 webpack 的 config 文件中,修改 CommonsChunkPlugin 的配置:
minChunks: 3
minChunks: 3 表示会把使用 3 次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件。
5) 图片压缩
- 使用合适的图片格式(WebP / AVIF);
- 通过 CDN 自动压缩;
- 小图标使用 SVG / iconfont。
6) 开启 GZip
拆完包之后,再用 gzip 做一下压缩。安装 compression-webpack-plugin:
npm i compression-webpack-plugin -D
在 vue.config.js 中引入并修改 webpack 配置:
const CompressionPlugin = require('compression-webpack-plugin')
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
config.mode = 'production'
return {
plugins: [new CompressionPlugin({
test: /.js$|.html$|.css/, // 匹配文件名
threshold: 10240, // 对超过 10k 的数据进行压缩
deleteOriginalAssets: false // 是否删除原文件
})]
}
}
}
在服务器端也要做相应的配置(Nginx 启用 gzip on; 并开启 gzip_static on;),浏览器请求时带上 Accept-Encoding: gzip,才会真正生效。
7) 使用 SSR
服务端渲染,组件或页面通过服务器生成 html 字符串,再发送到浏览器。从头搭建一个服务端渲染是很复杂的,Vue 应用建议使用 Nuxt.js 实现服务端渲染。
来源整理自:vue3js.cn 面试官系列



