前端性能优化(一) · 性能指标知多少

前端性能优化是个永恒的话题了,在性能优化前首先要了解网页当前的性能情况如何(性能水位),那依据什么来判断性能情况呢?这就要聊到性能指标了,本文主要是和大家分享性能指标有哪些、什么样的数值代表指标表现优越、如何去监测这些指标。

不过在进入主要内容之前,我们从为什么要做性能优化开始。

为什么要做性能优化

  1. 用户体验和转化率:网页流畅美观能够更吸引用户进入、停留(用户留存up)、完成购买流程,有利于增加收入(转化率up)。

研究表明,页面加载时间每增加1秒,转化率可能下降7%。------ from akamai

  1. SEO和流量增长:网站加载快能够提高搜索后的排名,从而增加潜用户的访问量(用增 up),有利于带来更多的广告收入(流量变现up)。
  2. 成本节约:优化后的网页通常会减少数据传输量,这可以降低服务器带宽的使用率,从而减低托管成本。
  3. 移动设备适应性:用户越来越倾向于通过手机上网,由于移动设备可能在网络连接质量方面受限,因此对移动端的性能优化尤其重要。

什么是好的性能

好产品的标准最直观的当然是用它的人评价"好用",所以我们从用户的角度切入,好的网页性能无非是三方面:内容展现快、交互流畅、视觉舒适。

这不就巧了嘛,刚好每个方面都有一个关键指标,三个指标合称为 CWV 。

CWV

CWV,全称(Core Web Vitals,核心 Web 指标),包含 LCP(最大内容渲染时间)、CLS(累计布局偏移指数)、INP(下次互动延迟时间) 三个指标,分别对应加载性能、交互性和视觉稳定性。

每个维度都有多个指标,但本文介绍的是每个维度最关键、最贴近用户感知的。凭啥是这仨?那就看文末的参考资料123吧
INP 是一个较新的指标,自 2024 年 3 月 12 日起替换 FID 作为新的 CWV 之一。交互维度的指标有还有 FID、TBT 也都比较重要,将作为补充在后面说明。

每个指标被定义为三个段位,值越小(趋于绿色)则说明越好。

关于指标等级划分时的临界值如何确定的,可看网页性能指标背后的科学研究

在实际的业务场景中,可能会综合这三个指标去自定义自己应用的性能指标,而自定义的基础是要了解这些指标背后的含义和计算逻辑。下面就逐一学习一下咯

LCP

最大内容绘制时间(Largest Contentful Paint,LCP),用于记录视窗内最大的元素绘制的时间。因为页面中的最大元素在渲染过程中可能会发生改变,所以该时间会随着页面渲染变化而变化(观察绿色区域是变化的),同时该指标会在用户第一次交互后停止记录。

逻辑:

渲染过程中,为最大元素分配 largest-contentful-paint 标识,直到用户与网页互动,然后按照时间,最近的具有 largest-contentful-paint 标识的元素渲染的时间作为 LCP 反馈。

INP

Interaction to Next Paint (INP) 衡量用户交互的延迟,会在网页生命周期内观察用户与网页进行的所有点击、点按和键盘互动的延迟时间,专注于记录用户交互发生时到下一次绘制(渲染更新)时的延迟,忽略离群值并报告最长持续时间。

  • 被观察的互动(交互)有哪些?使用鼠标点击、点按带有触摸屏的设备、按实体键盘或屏幕键盘上的某个键
  • 离群值是什么?每50次互动中延迟最长的那一次就是离群值,类似去掉最低分的意思

点击查看视频

观察上面的视频,这是网页声明周期内,一次互动延迟好(右侧)与差(左侧)的表现。

逻辑:

  1. 将用户的单个动作识别为一个互动,互动可能包含多个事件。
  2. 计算单个互动的时长,这里的时长主要是多个事件脚本的执行时间总和。
  3. 如果页面中有多个互动,每 50 次会忽略 1 次离群值。
  4. 当用户离开网页时,除去离群值,选择耗时最长的那个作为 INP 汇报。

单个互动的流程大概是这样的,由此图能看出单次的时间如何计算

CLS

如果两帧之间,无论是因为资源加载、用户交互还是动画效果,导致元素的位置发生了变化,这被视为布局偏移。累积布局偏移(Cumulative Layout Shift, CLS)是以会话为单位把会话内布局偏移进行累加后得分,再选出得分最高的会话的得分,作为最后的 CLS。

  • 什么是布会话?比较复杂,接着看"逻辑"小节就懂了
  • 不会产生布局偏移:css transform、position:fixed、overflow:scroll、
  • 可以手动排除的布局偏移:用户离散输入(和上一节INP中被观察的互动一样)后 500 毫秒内发生的布局偏移会打标机,开发者可以手动排除

查看视频

这里展示的是由于元素异步加入页面导致了布局偏移的例子,导致用户意外的点击了购买的按钮,本来用户想点击的是返回。

逻辑:

网页生命周期内,对每一次偏移进行得分计算(布局偏移分数 = 影响分数 * 距离分),累计到当前会话窗口。也可能新开启会话窗口,作为新会话窗口的初始值。对会话窗口累计得分进行比较,最大值则为 CLS。

  • 一次布局偏移分数 = 影响分数 * 距离分 (LS = impact fraction * distance fraction)
  • 一次会话累计偏移分数 = 同一会话时段所有布局偏移分数之和 S = ∑(LS_i)
  • CLS = 页面所有会话时段中最大的一次累计偏移分数 CLS = max(S_1,S_2,...,S_i,...,S_n)

一次偏移得分 LS = impact fraction * distance fraction)

  • 影响分数(impact fraction):红色虚线矩形表示这三个不稳定元素在偏移前后的面积,在本例中,大约为视口区域的 60%(影响比例为 0.60)。
  • 距离分数(distance fraction):箭头表示不稳定元素从起始位置移动的距离。以蓝色箭头表示的"斑马"元素移动幅度最大,移动了视口高度的 30% 左右。这会使此示例中的距离比例变为 0.3
  • 单次布局偏移的得分为 0.60 x 0.3 = 0.18

从 LS 到 CLS

查看视频

视频中,在网页的生命周期内,有多次会话,每次会话有多个连续偏移。

  • 连续:这些事件之间的间隔不超过1秒。这个"会话"会持续到没有新的布局偏移事件发生,或者直到会话的总时间达到5秒。
  • 会话结束的条件:没有新的偏移产生 或 一直有新的偏移继续加入但会话总时间达到5秒

FID和TBT 与 INP 的关系

FID

FID 仅测量了页面上首次互动的输入延迟。INP 通过考虑所有页面互动来改进 FID。

对于首屏来说可以直接用 FID ,FID 更倾向于一个加载维度的指标,如果考虑整体,需要用 INP。

TBT

TBT衡量的是所有阻塞页面响应用户输入的长任务的总时间。TBT关注的是加载期间出现的所有长任务,即介于FCP(First Contentful Paint)和TTI(Time to Interactive)之间的长任务。总体来讲,TBT反映了页面在加载期间无法快速响应用户输入的总时间。

TBT 是一个实验室指标(简单理解为开发时,开发人员在自己电脑上用的),比如用lighthouse去测首屏交互延迟时,没有FID,用TBT。

  • 一个"长任务"是指在主线程上执行的超过50毫秒的任何任务。

性能监控方案

上面我们已经对性能指标有了不少的了解,那我们如何衡量我们应用中的性能指标呢?

  • 在开发阶段,用lighthouse和WebPageTest。lighthouse统计本机的性能数据可以是首屏或者次屏,WebPageTest可以模拟具体的地区和设备通过API捕获首屏的性能数据,并且可以生成完整的网络请求时序图。
  • 在生产环境,需要收集真实用户的数据。最好的方案是采用类似 Sentry 这样的前端监控平台,除了性能指标,这类平台也可以方便查看接口错误、接口性能、JS错误的情况。而CrUX(chrome用户报告)则适合在已有的项目没有接入平台的情况下,查看历史数据,对初步了解网页性能是有很大帮助的。
  • 一般来说前端监控平台对性能指标的计算都是依赖于 web-vitals ,web-vitals 又是在 PerformanceObserver API 基础上封装的。
  • 而一些 chrome 自己的工具可能是浏览器的底层能力,可能没走 JS API。

PerformanceObserver API

js 复制代码
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'largest-contentful-paint', buffered: true});

和cwv相关的type值:

通过传参含义也可以看出 API 与 指标并不是划等号的,详情查看链接:
LCP

INP:由于INP太新了,type还没有对应的值,FID是有的。
CLS

web-vitals

如果用web-vitals倒是可以直接获取性能指标的值,这个库帮我们把API到指标的差异抹平(感觉用词不准,就这吧)了。

js 复制代码
import {onLCP, onFID, onCLS} from 'web-vitals';

onCLS(console.log);
onINP(console.log);
onLCP(console.log);

参考资料

相关推荐
前端小小王25 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发35 分钟前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀1 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪1 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef3 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6413 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js
dz88i84 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr4 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook