前言
- 此文是本人在公司的小项目开发中结合网上资料整理所得
- 文章主要以思路为主,细节可根据关键字另行检索
抛砖引玉:希望路过的巨佬能不吝赐教,指点一二优化手段
性能指标
作用
- 性能优化的最终目的是体验优化
- 体验优化太主观
- 不同人、不同设备、不同网络情况下的体验是不同
- 为了准确的监测性能情况,我们需要客观的、可量化的指标来说明性能状况
常用指标
- 首次内容绘制 (First Contentful Paint,FCP)
- 任意部分 DOM 完成渲染的耗时
- 最大内容绘制 (Largest Contentful Paint,LCP)
- 可视区域内尺寸最大的文字或图像渲染完成的耗时
- 会受 http 请求时间影响
- 首次输入延迟 (First Input Delay ,FID)
- 用户首次交互(如点击按钮、链接等)后到浏览器开始响应之间的时间间隔
- 受操作时机影响,完全加载后 FID 值就低
- 交互到绘制延迟(Interaction to Next Paint,INP)
- 在页面浏览过程中的所有交互(点击、键盘输入、触摸等)与浏览器渲染响应的整体延迟情况
- 累积布局偏移 (Cumulative Layout Shift,CLS)
- 监测的是所有非用户操作导致的布局偏移
- ex. 资源加载、动态内容插入等原因,导致的页面元素位置的意外变化
- 第一字节时间 (Time to First Byte,TTFB)
- 测量 HTTP 请求发送后,到接收到第一字节数据响应的耗时,通常包括重定向、DNS查询、服务器响应延迟等耗时
数据采集
- web-vitals
- web-vitals 是谷歌的 Chrome 维护团队于 2020 年开源的工具库,它基于统一的浏览器 Performance API 获取标准化的用户体验指标,可用于开发环境的数据采集
- Performance API
- Performance API 是一组在浏览器平台测量和收集性能数据的接口,它允许开发者访问页面加载、资源获取、用户交互、JavaScript执行等性能相关的数据
数据可视化监控工具
- Prometheus / Grafana
- 将采集到的定制化数据上报
小结
- 上述流程属于标准化性能监控流程
- 小公司小项目还是体感为主,配合 lighthouse 进行优化即可
请求优化
- 新版 http 协议
- ex.http2 的多路复用、头部压缩
- gzip 压缩
- 加快网络传输
- http 缓存
- 静态资源采用强缓存,其余采用协商缓存
- 资源优先级提示符
- ex. 预取回 Prefetch
- 用于提示浏览器在 CPU 和网络带宽空闲时下载指定 URL 的 JS,图片等各类资源,存储到浏览器本地缓存中
- 预取回的资源不会进一步解析、运行该资源
- cdn 配置
资源优化
- 资源压缩
- 资源懒加载
- 图片九宫格
- 图片格式:avif > webp > png/jpg
- GIF 转视频
- 视频格式有运动估计(Motion Estimation)、预测编码(Predictive Coding) 等专用的编码优化技术
- 可以实现相邻帧优化,对于视频中相邻的几帧图像,只需要保存帧与帧之间的部分差异像素数据
代码优化
- 移除全局变量
- 方便树摇
- 全量导入改为按需导入
- lodash -> lodash-es
- element plus
- 虚拟滚动
- 防抖 & 节流
产物优化
产物分析 rollup-plugin-visualizer
1、基础使用
pnpm add rollup-plugin-visualizer -D
// vite.config.ts
plugins: [
visualizer({
filename: 'stat.html', // 生成的可视化报告的文件名
emitFile: true, // 是否将报告文件作为输出文件的一部分
gzipSize: false, // 是否显示 gzip 压缩后的文件大小
brotliSize: false, // 是否显示 Brotli 压缩后的文件大小
open: true // 是否在生成报告后自动打开浏览器
})
]
2、筛选
- 筛选 bundle 和 文件
xxx-*.js:*/**/xxx.js
- 筛选 bundle
*/xxx-*.js:
- 筛选文件
*/**/xxx.js
优化思路
- 入口用到的 npm 包与业务代码剥离,打包成 vendor chunk,降低http请求,提升缓存效率
- 仅需对产物文件根据大小进行排序,对较大的包进行分析
- 不常用的 npm 包可尝试动态导入
- 较大的组件,例如对 wangEditor 封装过的组件可采用异步组件的方式进行导入
- 坑点:做异步路由时,如使用到 import.meta.glob 需注意范围,涉及到的文件全都会被单独拆分