侧边栏壁纸
  • 累计撰写 8 篇文章
  • 累计创建 8 个标签
  • 累计收到 0 条评论

Web Vitals —— 谷歌的新一代 Web 性能体验和质量指标

Xiu Bug
2022-01-01 / 0 评论 / 1 点赞 / 2,157 阅读 / 4,900 字
温馨提示:
本文最后更新于 2022-03-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

RAIL 模型

RAIL 是一种以用户为中心的性能模型。每个网络应用均具有与其生命周期有关的四个不同方面,且这些方面以不同的方式影响着性能:
image.png

  1. 响应:输入延迟时间(从点按到绘制)小于 100 毫秒。
    用户点按按钮(例如打开导航)。
  2. 动画:每个帧的工作(从 JS 到绘制)完成时间小于 16 毫秒。
    用户滚动页面,拖动手指(例如,打开菜单)或看到动画。 拖动时,应用的响应与手指位置有关(例如,拉动刷新、滑动轮播)。 此指标仅适用于拖动的持续阶段,不适用于开始阶段。
  3. 空闲:主线程 JS 工作分成不大于 50 毫秒的块。
    用户没有与页面交互,但主线程应足够用于处理下一个用户输入。
  4. 加载:页面可以在 1000 毫秒内就绪。
    用户加载页面并看到关键路径内容。

如果要提升网站用户体验,RAIL 是个不错的评估模型。

Web 性能指标

FMP

First Meaningful Paint 是指页面的首要内容出现在屏幕上的时间。

FMP 之怪现象

目前尚无标准化的 FMP 定义,因此也没有性能条目类型。 部分原因在于很难以通用的方式确定「有效」对于所有页面意味着什么。但是,一般来说,在单个页面或单个应用中,最好是将 FMP 视为主角元素呈现在屏幕上的时刻。

所以,我们经常会面临这样的问题:
image.png
FCP 在可接受范围,但是 FMP 却完全失控。
也可能是这样的问题:
image.png
或许还有这样的问题:
image.png
为什么结构类似的站点, FMP 加载却千差万别。要了解 FMP 我们需要知道它的计算规则,下面让我们一层层抽丝剥茧。

刨根问底

究竟是什么导致 FMP 的时机差距如此之大?我们先来看一张官方图片:
image.png
如果 FCP 是 1.5s , FMP 是 3s ,那么 FCP 分数将会是 99 ,但是 FMP 分数将是 75 。

除了上述影响外,我们还需要关注 Lighthouse V3 的记分规则:
image.png
虽然 FMP 权重仅为 1 ,很遗憾,因为如上规则的存在,我们站点无法到达满分💯。

确定页面上最关键的主角元素之后,我们应确保初始脚本加载仅包含渲染这些元素并使其可交互所需的代码。

重要结论

了解了相关计算规则之后,我们继续来剖析 FMP 。对于不同的站点,首要内容是不同的,例如:

Google
需要注意的是,通常首要内容是不包括 Headers 和导航条的。

为了方便理解,我们用一个简单的公式表示:「首次有效绘制 = 具有最大布局变化的绘制」。

很好,此刻我们不用纠结「首次有效渲染」了,转而去了解「最大布局变化的绘制」。基于 Chromium 的实现,这个绘制是使用 LayoutAnalyzer 进行计算的,它会收集所有的布局变化,当布局发生最大变化时得出时间。

具有最大布局变化的绘制如何计算呢?

1.侦听页面元素的变化;

2.遍历每次新增的元素,并计算这些元素的得分总;

3.如果元素可见,得分为 1 * weight ,如果元素不可见,得分为 0 ;

还是很抽象,我们继续探索 LayoutAnalyzer ,在源码中得到如下公式:

布局显著性 = 添加的对象数目 / max(1, 页面高度 / 屏幕高度)

这下清晰了,既然可以算出来,那么优化 FMP 指日可待。从上面可以看出来「布局显著性」是通过添加对象数目与页面高度来计算的。似乎对象数目成了解决问题的关键节点。

我们继续探索,如何去找到对象数目呢?很简单,打开 Chrome DevTool ,切到 Layout 面板。如果你还不会使用 Layout 面板,可以先看看网站优化工具。

这里就不展开了,直接上图,看看红框部分:
image.png
惊不惊喜,我们精简 DOM 似乎可以将公式中分子变小,或者让页面高度大于屏幕高度。到这里,所有谜团都解开了,优化 FMP 也就变得毫无挑战了。

总结

我们从扑簌迷离的 FMP 表象一层层找到了 Lighthouse 的记分规则,又从 Tracing Results 中得知最大布局变化的计算规则,因此转向 LayoutAnalyzer 源码研究,最终找到 Layout Objects ,从而解决了问题。虽然波折,但结局符合预期。

最后,我们再啰嗦一下,以下结论对理解 FMP 很重要:

Google
如果你觉得还是过于麻烦,不妨去试试 SSR 吧, FMP 将不再是烦恼。

解读 TTI(页面可交互时间)

TTI 指的是应用既在视觉上都已渲染出了,可以响应用户的输入了。要了解 TTI ,我们需要知道它的计算规则,先来看下面这张图:
image.png

我们可以简单的理解一下:

  1. First Idle 是主线程处于静止状态且浏览器已完成 First Meanfulful Paint 的第一个早期迹象;

  2. TTI 在 FMP 之后,浏览器主线程静止至少 5s ,并且没有可以阻断用户交互响应的「长任务」。

如何计算 TTI?

在计算之前,我们先来看一下 Timing API :
image.png
我们可以通过 domContentLoadedEventEnd 来粗略的进行估算:
// 页面可交互时间
TTI: domContentLoadedEventEnd - navigationStart,
domContentLoadedEventEnd :文档的 DOMContentLoaded 事件的结束时间。

TTI 指标监控

我们可以通过 Google TTI Polyfill 来对 TTI 进行监测。
1.安装

npm install tti-polyfill

2.使用

import ttiPolyfill from './path/to/tti-polyfill.js';
ttiPolyfill.getFirstConsistentlyInteractive(opts).then((tti) => {
  // Use `tti` value in some way.
});

Long Task(长任务)

对于「长任务」,我们通过如下图示说明:
image.png

对于用户而言,任务耗时较长表现为滞后或卡顿,而这也是目前网页不良体验的主要根源。
如何测量 Long Task ?

// Jartto's Demo 
const observer = new PerformanceObserver((list) => { 
	for (const entry of list.getEntries()) { 
		// TODO... 
		console.log(entry); 
	} 
}); 
observer.observe({entryTypes: ['longtask']});

控制台输出结果如下:

{ 
	"name": "self", 
	"entryType": 
	"longtask", 
	"startTime": 315009.59500001045, 
	"duration": 99.9899999878835, 
	"attribution": [ { 
		"name": "unknown", 
		"entryType": "taskattribution", 
		"startTime": 0, 
		"duration": 0, 
		"containerType": "window", 
		"containerSrc": "", 
		"containerId": "", 
		"containerName": "" 
	} ] 
}

Long Tasks API 可以将任何耗时超过 50 毫秒的任务标示为可能存在问题,并向应用开发者显示这些任务。 选择 50 毫秒的时间是为了让应用满足在 100 毫秒内响应用户输入的 RAIL 指导原则。

实际开发过程中,我们可以通过一个 hack 来检查页面中「长任务」的代码:

// detect long tasks hack 
(function detectLongFrame() { 
	let lastFrameTime = Date.now(); 
	requestAnimationFrame(function() { 
		let currentFrameTime = Date.now(); 
		if (currentFrameTime - lastFrameTime > 50) { 
			// Report long frame here... 
		} 		
		detectLongFrame(currentFrameTime); 
	}); 
}());

Core Web Vitals

Core Web Vitals是Web Vitals的一个子集,适用于所有网页,应该被所有开发者去进行测量,也将在所有Google提供的工具中浮现。每一个Core Web Vitals都代表了用户体验独特的一面,可以用现场数据测试,能反映出以用户为核心的关键结果的真实体验。

构成Core Web Vitals的核心指标,将随着时间的推移而发展。当下2020年我们仅仅关注三个方面: 加载、可交互性和视觉稳定性。包含以下指标(以及各自的阈值):

(Loading)
(Interactivity)
(Visual Stability)

  • Largest Contentful Paint (LCP): 衡量加载性能。为了提供一个好的用户体验,LCP应该在2.5秒内。
  • First Input Delay (FID): 衡量可交互性。为了提供一个好的用户体验,FID应该在100毫秒内。
  • Cumulative Layout Shift (CLS): 衡量视觉稳定性。为了提供一个好的用户体验,CLS应该小于0.1。

对上面每一个指标而言,为了保证覆盖到大部分用户,一般阈值设置在75%的页面加载达标即可,包括手机和pc站点。

JavaScript中测试Core Web Vitals

每一个Core Web Vitals都可用JS提供的Web API来测试。

最简单的方式,就是集成 Web Vitals 的js库,这是Google提供的一个小型可以生产环境使用的统计性能的库,涵盖了基本所有指标。

import {getCLS, getFID, getLCP} from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
      fetch('/analytics', {body, method: 'POST', keepalive: true});
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);

如果不需要发送统计,可以直接使用Web Vitals 扩展 ,这个扩展其实就是集成了上面提到的js库,实时的展现每个页面的指标值。

Core Web Vitals实验数据测试工具

一般可以用开发者工具和Lighthouse,这两个都能测试FCP和CLS,但FID无法测试,可以用TBT替代。

其他Web Vitals

除了核心之外,还有其他类型的Web Vitals,当然这些一般都是核心的补充,为一些特定的场景提供服务。

例如,Time to First Byte (TTFB) 和 First Contentful Paint (FCP) 都是关于加载性能的,两者都有助于诊断LCP (缓慢的服务端响应,或者渲染阻塞的资源)。

同上,Total Blocking Time (TBT) 和 Time to Interactive (TTI) 则是影响FID的实验性指标,他们不属于核心,因为不能测试现场数据,不能反映用户为核心的关键结果。

不断发展的Web Vitals

Web Vitals 和 Core Web Vitals代表了当今开发人员用来衡量我们整个Web体验质量的最佳可用信号,但这些信号还不完善,未来会有更多改善和提升的点。

总结

Core Web Vitals是与用户为中心的理念密切相关的指标,一般不会怎么变化,相对稳定,一旦发生改变,或者阈值有了调整,影响很大,开发者需要对这种更新有预知或者年度的预测。

而其他的Web Vitals经常是辅助Core Web Vitals的,可能比其更具有实验性。因此,它们的定义和阈值的改动可能会很频繁。

0

评论区