前端性能优化

403 阅读7分钟

性能指标

RAIL模型,以用户为中心的性能指标

RAIL 是一种以用户为中心的性能模型,它提供了一种考虑性能的结构。该模型将用户体验分解到按键操作(例如,点击、滚动、加载)中,帮助您为每个操作定义性能目标。
RAIL 代表 Web 应用生命周期的四个不同方面:响应、动画、空闲和加载。用户对这些上下文分别有不同的性能期望,因此,性能目标是根据上下文以及用户如何感知延迟的用户体验研究来定义的。

image.png

Core Web Vitals

Core web vitals 是长期下来根据大量使用者体验所制定的指标。

2020 年的核心 Web 指标为三大指标:Largest Contentful Paint 最大内容绘制 (LCP)、First Input Delay 首次输入延迟 (FID) 和 Cumulative Layout Shift 累积布局偏移 (CLS)。每项指标测量用户体验的不同方面:LCP 测量感知加载速度,并在页面的主要内容基本加载完成时,在页面加载时间轴中标记出相应的点;FID 测量响应度,并将用户首次尝试与页面交互的体验进行了量化;CLS 测量视觉稳定性,并对可见页面内容的意外布局偏移量进行了量化。

最大内容渲染(LCP:Largest Contentful Paint)

测量从页面开始加载到最大文本块或图像元素出现在屏幕中的时间,用于感知页面加载速度的指标

image.png

如何改进LCP:

首次输入延迟(FID:First Input Delay)

测量从用户第一次与您的网站交互到浏览器实际能够运行所需的时间,用于测量页面加载响应度的指标

image.png

如何改进FID:

累积布局偏移(CLS: Cumulative Layout Shift)

测量在整个页面生命周期中所有意外的布局偏移的累积分数,用于测量视觉稳定性的指标

image.png

如何改进CLS:
对于大多数网站来说,您可以通过遵循一些指导原则来避免所有的意外布局偏移:

  • **始终在您的图像和视频元素上包含尺寸属性,或者通过使用CSS 长宽比容器之类的方式预留所需的空间。**这种方法可以确保浏览器能够在加载图像期间在文档中分配正确的空间大小。请注意,您还可以使用unsized-media 功能策略在支持功能策略的浏览器中强制执行此行为。
  • **除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容。**这样能够确保发生的任何布局偏移都在预期之内。
  • **首选转换动画,而不是触发布局偏移的属性动画。**动画过渡的目标是提供状态与状态之间的上下文连续性。

Web Vitals

首次内容渲染(FCP:First Contentful Paint)

测量从页面开始加载到页面中任意内容出现在屏幕上的时间,用于感知页面加载速度的指标

image.png

如何改进FCP:

可交互时间(TTI:Time to Interactive)

测量从页面开始加载到内容完成渲染、初始脚本完成加载后,页面能够快速、可靠地响应用户输入所需的时间,用于测量页面加载响应度的指标

如需根据网页的性能跟踪计算 TTI,请执行以下步骤:

  1. 先进行First Contentful Paint 首次内容绘制 (FCP)
  2. 沿时间轴正向搜索时长至少为 5 秒的安静窗口,其中,安静窗口的定义为:没有长任务且不超过两个正在处理的网络 GET 请求。
  3. 沿时间轴反向搜索安静窗口之前的最后一个长任务,如果没有找到长任务,则在 FCP 步骤停止执行。
  4. TTI 是安静窗口之前最后一个长任务的结束时间(如果没有找到长任务,则与 FCP 值相同)。

下图将有助于您更直观地理解上述步骤:

image.png

为了提供良好的用户体验,网站在普通移动硬件上进行测试时,应该努力将可交互时间控制在5 秒以内。

总阻塞时间(TBT:Total Blocking Time)

测量FCP和TTI之间,主线程被阻塞而无法响应用户输入的总时间,用于测量加载响应度的指标

image.png

image.png

如何改进TBT:

检测方式

Lighthouse

Chrome Dev Tools

Performance API

Navigation Timing

performance.getEntriesByType("navigation");

image.png

计算:
重定向次数:performance.navigation.redirectCount
重定向耗时: redirectEnd - redirectStart
DNS 解析耗时: domainLookupEnd - domainLookupStart
TCP 连接耗时: connectEnd - connectStart
SSL 安全连接耗时: connectEnd - secureConnectionStart
网络请求耗时 (TTFB): responseStart - requestStart
数据传输耗时: responseEnd - responseStart
DOM 解析耗时: domInteractive - responseEnd
资源加载耗时: loadEventStart - domContentLoadedEventEnd
首包时间: responseStart - domainLookupStart
白屏时间: responseEnd - fetchStart
首次可交互时间: domInteractive - fetchStart
DOM Ready 时间: domContentLoadEventEnd - fetchStart
页面完全加载时间: loadEventStart - fetchStart
http 头部大小: transferSize - encodedBodySize

Resource Timing

performance.getEntriesByType("resource");

image.png

计算:

[...performance.getEntriesByType('resource')].forEach(resource => {
    console.log(`${resource.initiatorType}(${resource.name})加载耗时:${resource.responseEnd - resource.startTime}`);
});

paint Timing

首屏渲染时间、首次有内容渲染时间 计算:

const observer = new PerformanceObserver(function(list) {
const perfEntries = list.getEntries();
// eslint-disable-next-line no-restricted-syntax, no-underscore-dangle
for (const _perfEntry of perfEntries) {
    console.log('_perfEntry: ', _perfEntry);
}
});
// register observer for paint timing notifications
observer.observe({entryTypes: ["paint"]});

image.png

性能优化策略

加载性能

目标

  • 划分资源优先级,优先关键资源
  • 精简传输内容,合理利用缓存

方法

  • 网络优化
    1. 升级HTTP2协议(二进制分帧层、多路复用、头部压缩、服务端推送)
    2. 使用CDN网络
    3. HTTP缓存头(协商缓存、强缓存)
    4. Gzip/Brotli
    5. DNS Prefetch
    6. 离线缓存ServiceWorker
  • 资源优化
    1. 静态资源HTML/CSS/JS压缩
    2. 静态资源按需引入(Tree Shaking、Webpack Dynamic import)
    3. 资源预加载(Preload、Prefetch)
    4. 第三方JS延迟加载(defer、async)
    5. 图片文件大小(压缩、合并小图转base64、简单图片用css3处理)
    6. 图片webp
    7. 图片懒加载
    8. 图片渐进式渲染
    9. 图片逐步加载(先加载缩略图,再加载完整图片)
    10. 视频压缩、下一代视频格式(AV1)
    11. 字体(预加载、精简、渲染策略(font-display、FontFaceObserver))

运行体验

目标

  • 及时给用户反馈
  • FPS保持60帧

方法

  • 渲染优化
    1. 减少重排重绘
    2. content-visibility部分渲染
    3. 提升合成层,开启GPU渲染
    4. 减少合成层的尺寸
  • 渲染体验
    1. Loading效果
    2. CSR VS SSR
  • 避免长任务
    1. 防抖、节流
    2. WebWorker
    3. 事件委托
    4. 手动拆分代码任务,分多帧执行
    5. 缓存场任务结果,避免二次执行

其他

  • 减少不必要的重定向
  • 缓存接口数据
  • 减少DOM数量
  • 避免图片src为空
  • 减少cookie大小
  • 静态资源使用无cookie域名
  • 使用体积小,可缓存的favicon.ico
  • 减少DOM操作
  • 使用<link>替代@import
  • 不要使用filter
  • 不要覆盖原生方法
  • 尽量避免添加大量的JS动画,CSS3动画和 Canvas 动画都比 JS 动画性能好。
  • 使用requestAnimationFrame来代替setTimeoutsetInterval,因为requestAnimationFrame可以在正确的时间进行渲染,setTimeout 和setInterval无法保证渲染时机。不要在定时器里面绑定事件。
  • 使用字体图标 iconfont 代替图片图标
  • 降低 CSS 选择器的复杂性
  • 提取公共代码
  • 模板预编译
  • 合理使用v-ifv-show
  • 合理使用watchcomputed
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 服务端渲染 SSR or 预渲染

参考