-
Notifications
You must be signed in to change notification settings - Fork 25
Description
常见web 性能指标
- 首次绘制时间 (FP) - 即 First Paint,为首次渲染的时间点
- 首次内容绘制时间(FCP):即First Contentful Paint,为首次有内容渲染的时间点
- 首次有效绘制时间(FMP) - 即First Meaning Paint,用户启动页面加载与页面呈现首屏之间的时间
- Dom ready - domContentLoadEventEnd - fetchStart
- 页面完全加载耗时 - 页面完全加载的时间(load事件发生的时间)
- 完全可交互时间 (TTI) - 即Time to interactive,记录从页面加载开始,到页面处于完全可交互状态所花费的时间
如果你经常做网站优化,可能会陷入一个性能指标的泥潭即「面向指标优化」。真正的用户体验从来不是指标决定,相反它应该最真实的反映用户行为。
所以本节我们就来研究 TTI(Time to Interactive)
,话题展开之前,我们先来了解一些背景知识。
推荐 https://web.dev/,google 面向开发者推荐的,给 Web应用指导和分析。
一、RAIL 模型
RAIL
是一种以用户为中心的性能模型。每个网络应用均具有与其生命周期有关的四个不同方面,且这些方面以不同的方式影响着性能:
1.响应:输入延迟时间(从点按到绘制)小于 100 毫秒。
用户点按按钮(例如打开导航)。
2.动画:每个帧的工作(从 JS 到绘制)完成时间小于 16 毫秒。
用户滚动页面,拖动手指(例如,打开菜单)或看到动画。 拖动时,应用的响应与手指位置有关(例如,拉动刷新、滑动轮播)。 此指标仅适用于拖动的持续阶段,不适用于开始阶段。
3.空闲:主线程 JS 工作分成不大于 50 毫秒的块。
用户没有与页面交互,但主线程应足够用于处理下一个用户输入。
4.加载:页面可以在 1000 毫秒内就绪。
用户加载页面并看到关键路径内容。
如果要提升网站用户体验,[RAIL](Measure performance with the RAIL model) 是个不错的评估模型。
二、解读 TTI(页面可交互时间)
TTI(页面可交互时间)度量标准衡量页面在用户可以与之交互之前所花费的时间,即页面对点击做出响应之前的时间。这取决于何时:
-
页面显示有用的内容(由第一个有内容的绘图确定)
-
页面上最可见的内容是交互式的(可单击或响应鼠标悬停时,等等)
-
页面在最长 50 毫秒内响应用户交互
在实践中,用于计算TTI的算法同时观察瀑布和网络活动。确定互动时间的三个阶段是:
-
选择一个相关的起点,例如第一个
Contentful Paint
或domcontentloadended
; -
从这一点出发,寻找一个网络稳定 5 秒的窗口(不包含长任务);
-
一旦发现了这一点,回顾最后一个长任务的结束,并将该时间报告为“交互时间”。
这意味着在文档分析完成之前不可能估计 TTI。要了解 TTI
,我们需要知道它的计算规则,先来看下面这张图:
在官方文档中找到了如下描述:
First Idle is the first early sign of time where the main thread has come at rest and the browser has completed a First Meaningful Paint.
Time to Interactive is after First Meaningful Paint. The browser’s main thread has been at rest for at least 5 seconds and there are no long tasks that will prevent immediate response to user input.
我们可以简单的理解一下:
1.First Idle
是主线程处于静止状态且浏览器已完成 First Meanfulful Paint
的第一个早期迹象;
2.TTI
在 FMP
之后,浏览器主线程静止至少 5s
,并且没有可以阻断用户交互响应的「长任务」。
如果你对 FMP
还不了解,不妨先看看这篇文章:网站性能指标 - FMP
。除此之外,第二条中提到的「长任务」又是什么呢?
三、Long Task(长任务)
对于用户而言,任务耗时较长表现为滞后或卡顿,而这也是目前网页不良体验的主要根源。
如何测量 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);
});
}());
四、如何计算 TTI?
Google` 官方文档中有一段描述:
`Note: Lower Bounding FirstInteractive at DOMContentLoadedEnd DOMContentLoadedEnd is the point where all the DOMContentLoaded listeners finish executing. It is very rare for critical event listeners of a webpage to be installed before this point. Some of the firstInteractive definitions we experimented with fired too early for a small number of sites, because the definitions only looked at long tasks and network activity (and not at, say, how many event listeners are installed), and sometimes when there are no long tasks in the first 5-10 seconds of loading we fire FirstInteractive at FMP, when the sites are often not ready yet to handle user inputs. We found that if we take max(DOMContentLoadedEnd, firstInteractive) as the final firstInteractive value, the values returned to reasonable region. Waiting for DOMContentLoadedEnd to declare FirstInteractive is sensible, so all the definitions introduced below lower bound firstInteractive at DOMContentLoadedEnd.
所以,我们可以通过 domContentLoadedEventEnd
来粗略的进行估算:
// 页面可交互时间
TTI: domContentLoadedEventEnd - navigationStart,
domContentLoadedEventEnd:文档的 DOMContentLoaded 事件的结束时间。
The domContentLoadedEventEnd attribute MUST return a DOMHighResTimeStamp with a time value equal to the time immediately after the current document's DOMContentLoaded event completes.
如果你觉得上述计算过于复杂,可以通过 Google
实验室提供的 Polyfill
来获取。
五、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.
});
Other Resources
Measure performance with the RAIL model
First Interactive and Consistently Interactive
User-centric performance metrics
Focusing on the Human-Centric Metrics