性能是相对的。不同用户的设备、网络,不同网站的视觉反馈,交互体验决定它们的性能必然是不一样的。
改进web性能的前提是确定当前的性能如何?这便产生了如何衡量性能的问题。总的来说有两种方式:
必须要说这两种方式没有优劣之分,你应该同时使用这两种方式来改进你的网站。
TTFB衡量的是从请求资源的最开始到响应的第一个Byte数据收到的用时。就是下图的starTime到responseStart的时间
如何计算TTFB呢?分两种场景
new PerformanceObserver((entryList) => {
const navs = entryList.getEntriesByType('navigation');
//获得navigation的responseStart时间
console.log(`TTFB用时: ${navs[0].responseStart}`);
}).observe({ type: 'navigation', buffered: true });
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
for (const entry of entries) {
//资源被缓存,responseStart=0
//请求跨域资源,响应头没有Timing-Allow-Origin字段
if (entry.responseStart > 0) {
console.log(`资源TTFB用时: ${entry.responseStart}`, entry.name);
}
}
}).observe({
type: 'resource',
buffered: true,
});
FCP计算的是从页面开始加载到有部分内容被渲染。内容被渲染强调了两点:一是元素必须有内容,不能是空标签;二是必须被渲染,不计算display:none和visibility: hidden这些未显示的元素。可以通过如下代码计算FCP时间:
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntriesByName('first-contentful-paint')) {
console.log('FCP用时:', entry.startTime);
}
}).observe({ type: 'paint', buffered: true });
实际场景要比这复杂,推荐使用web-vitals开源库
LCP衡量页面主体内容渲染的时间。主体内容被渲染意味着对用户来说,视觉上可见,交互上可用。旧时我们使用load或DOMContentLoaded事件来衡量类似指标,但两者不好的地方在于它们不关注页面的实际渲染,关注的是加载是否完成。LCP关注的是主体/最大的元素是否被渲染。
目前这些元素是最大元素的考虑对象:
页面渲染是逐渐进行的,所以在渲染过程中被认为是最大的元素会不断变化。只要最新被认为的最大元素比上次的大,就会再报告一次largest-contentful-paint。
可以通过如下代码计算LCP。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP用时:', entry.startTime, entry);
}
}).observe({ type: 'largest-contentful-paint', buffered: true });
FID衡量用户操作视图到交互可以被处理、能够响应的时间。看下图
除了显式给元素添加事件侦听器,对有些元素的操作响应也要等到浏览器主线程空闲。
只有用户的离散输入比如,点击,触摸,按键才会被认为是首次输入。而缩放、滚动被认为是连续操作不算做首次输入。离散操作对应RAIL模型的R(响应),连续操作对应A(动画)。
假想一下,用户在主线程正忙的时候输入,那用户肯定会等待较长时间才能响应。假设用户在主线程空闲的时候再输入,那用户几乎不要等待。因为强依赖用户首次输入的时机,所以FID的测量值可能有巨大差别。
可以通过如下代码计算FID。当用户有离散输入时,PerformanceObserver的回调就会被触发。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
const delay = entry.processingStart - entry.startTime;
console.log('FID用时:', delay, entry);
}
}).observe({ type: 'first-input', buffered: true });
TTI衡量的是从最开始到能够响应用户输入的时间。那如何算是能够响应呢?这里有一个认定步骤。
TTI和FID都是衡量用户交互的指标。明显的FID的计算要简单明了,因为FID一定要有用户的参与。而TTI是理论上计算出的能够响应用户的时间点。
更快的渲染和更快的交互,是需要平衡的。更快的渲染意味着页面可能已经呈现了可操作的元素,但实际上因为主线程还在忙着处理渲染,根本无法操作这些元素。 所以要尽量缩小FCP和TTI之间的差值。
TBT计算的是FCP到TTI之间,主线程被阻塞无法响应用户交互的时间。
如果一个任务用时S超过50ms,这个任务就被认为是长任务。S-50ms的部分被认为是这个长任务导致主线程阻塞的时间。所以TBT算的是FCP到TTI之间,所有长任务减去50ms的时间之和。
请看下面两张示意图:
你是否曾经历过在网上阅读一篇文章,结果页面上的某些内容突然发生改变?文本在毫无预警的情况下移位,导致你找不到先前阅读的位置。或者更糟糕的情况:你正要点击一个链接或按钮,但在你手指落下的瞬间,诶?链接移位了,结果你点到了别的东西!
CLS,累积布局偏移,测量整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移。
详情移步这里。