性能优化
从输入 URL 到页面加载完成,经历了下面几个步骤,针对这些步骤逐个优化:
优化点 | 问题 | 解决 |
---|---|---|
DNS解析 | 减少DNS解析次数,把解析前置? | DNS缓存和DNS prefetch |
TCP连接 | TCP三次握手,时间太久? | 长连接,预连接,接入SPDY协议 |
HTTP请求 | HTTP请求怎么优化? | 减少请求次数和请求体积,CDN等网络层面的性能优化 |
浏览器渲染 | 拿到内容后,如何加快响应速度? | 资源加载优化、服务端渲染、浏览器缓存机制、DOM树构建、网页排版和渲染、回流和重绘、DOM操作合理规避等问题。 |
总的来说,前端性能优化可以分为 网络层面 和 渲染层面 两个大点。
终极方案: 服务器渲染SSR
1. 网络层面
1.1 HTTP
浏览器缓存
- 对于不需要缓存的资源,使用Cache-control: no-store
- 对于频繁变动的资源,使用Cache-control: no-cache并配合ETag,表示该资源已被缓存,但是每次都会发送请求询问资源是否需要更新
- 对于代码文件,通常使用Cache-control: max-age=31536000强缓存,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件
文件压缩
在request header中加上accept-encoding:gzip
资源部署
- 多个地址: HTML部署到
www.example.org
,而把js, css静态资源分离到static.example.org
。最大化并行下载(浏览器限制6个) - cdn加速
- 多个地址: HTML部署到
1.2 Webpack优化
- 分包
- 懒加载
- 减少体积过大的模块
- 压缩,tree shaking,
- 图片优化
- 图片压缩: 适当缩小尺寸、分辨率、选择高压缩率的格式,有损格式应该用 JPEG,无损格式应该用 Webp 格式。合并照片
- 小图片使用base64
- 图标使用svg,使用svgo压缩
- 代码绘制代替图片:一些可以用代码画出来的素材,渐变大背景图
2. 渲染层面
- 渲染优化
- 减少页面重排、重绘:回流、重绘、合成
- 少用高性能CSS属性:浮动、定位
- 选择器优化:避免层级过深,尽量不适用tag选择器,因为CSS选择符是从右到左进行匹配的,会匹配到很多元素
- 事件委托:避免太多事件处理器被添加到DOM元素上
- 虚拟列表:避免页面元素太多造成卡顿
- 内存管理:及时解除事件监听、清除定时器,避免内存泄漏
- ServiceWorker:开销大的操作新开一个线程操作,避免造成卡顿
- 事件防抖和节流
- 防抖:事件被触发n秒再执行,如果在这n秒内又被触发,则重新计时
- 节流:规定在一定时间内只能触发一次函数
- 组件级优化
- 减少使用内联函数的写法
v-click="() => {}"
,减少不必要的渲染 useMemo
,useCallback
,v-once/v-memo
手动标记缓存组件和计算结果,也是为了减少不必要的渲染- vue3中使用
shallowRef
代替放弃深层响应性,只有.value
的访问被追踪。浅层ref可避免大型数据的响应性开销
- 减少使用内联函数的写法
- 减少http请求: vuex, redux保留全局数据
- 组件懒加载:
() => import()
异步加载组件
10.用ImmerJS: 使用Proxy
实现了数据拷贝,数据修改开销更小
3. 性能监控指标
指标 目标值 测量工具
首屏加载时间(FCP) <1.5 秒 Lighthouse
最大内容绘制(LCP) <2.5 秒 Web Vitals
输入延迟(INP) <200 毫秒 Chrome DevTools
JavaScript 执行时间 <300 毫秒 Performance 面板