测量页面的加载性能是一项艰难的任务。因此 Google Developers 正和社区一起致力于渐进式网页指标(Progressive Web Metrics,简称 PWM’s)。
PWM’s 都是些什么,我们为什么需要它们?
先来讲一点关于浏览器指标的历史。
此前我们有两个主要的点(事件)来测量性能:
DOMContentLoaded
— 页面加载时触发,但脚本文件刚刚开始执行。
load
事件在页面完全加载后触发,此时用户已经可以使用页面或应用。
举个例子,如果我们看一下 reddit.com 的跟踪时间轴(Chrome 的开发者工具可以帮助我们用蓝色和红色的垂直线来标记那些点),就可以明白为什么这些指标不是那么有用了。
时至今日,我们也可以看到
window.onload
并没有像以前那样真实反映出用户的感知。参考:Steve Souders,Moving beyond window.onload() (2013)
确实,DOMContentLoaded
的问题在于解析和执行 JavaScript 的时间,如果脚本文件太大,那么这个时间就会非常长。比如移动设备,在 3G 网络的限制下测量跟踪时间轴,就会发现要花费差不多十秒才能到达 load
点。
另一方面,load
事件太晚触发,就无法分析出页面的性能瓶颈。
所以我们能否依赖这些指标?它们到底给我们提供了什么信息?
而且最主要的问题是,从页面开始加载直至加载完成,用户如何感知这个过程?
为什么加载感知会如此重要?可以参考 Chrome Developers 上的一篇文章:Leveraging the Performance Metrics that Most Affect User Experience,再次强调了加载问题。
看一下下方柱状图,X 轴展示了加载时间,Y 轴展示了体验到加载时长在特定时间区间里的用户的相对数量,你就可以明白不是所有用户的体验加载时间都会小于两秒。
因此在我们的试验里,17 秒左右的 load
时间在获取用户感知加载这方面是没有什么价值的。用户在这 17 秒里到底看到了什么?白屏?加载了一半的页面?页面假死(用户无法点击输入框或滚动)?如果这些问题有答案的话:
所以,大家都在尝试解读用户的想法并预测用户在这 17 秒的加载时间里会想些什么。
我的网页浏览开始了吗(服务器有回应,等等)?
Has my navigation started successfully (the server has responded, etc.)?
页面上是否有足够关键的内容使我能够理解?
我能不能和页面互动了呢?还是它依旧处于加载状态?
是否没有滚动卡顿、动画卡顿、无样式内容闪烁和缓慢的 Web 字体文件加载等问题出现,让我感到惊喜?
如果 DOMContentLoaded
或者 load
指标不能回答这些问题,那么什么指标可以回答?
PWM’s 的指标列表目的在于帮助检测性能瓶颈。除开 load
和 DOMContentLoaded
,PWM’s 给开发者提供了更多更详细的关于页面加载的信息。
下面让我们用 reddit.com 的跟踪时间轴来探究一下 PWM’s,并尝试弄明白每个指标的意思。
我曾经说我们只有两个指标,这其实不太准确。(Chrome)开发者工具还给我们提供了一个指标 - FP。这个指标表示页面绘制的时间点,换句话说它表示当用户第一次看到白屏的时间点(下面是 msn.com 的 FP 截屏)。可以在规范说明里阅读更多相关内容。
想弄明白它是如何工作的话,作为例子,我们可以看一下 Chromium 图层的底层原理。
FP 事件在图层进行绘制的时候触发,而不是文本、图片或 Canvas 出现的时候,但它也在列表里给出了一些开发者尝试使用的信息。
但它并不是标准指标,所以测量就变得非常棘手。因此用到了一些不同的 “取巧” 技术,比如:
requestAnimationFrame
使用DOMContentLoaded
和 load
事件(它们的问题之前已经讲过)但是,尽管做出了这些努力,它仍然不具有太大的价值,因为文本、图片和 Canvas 可能在 FP 事件触发没多久就会进行绘制,而这些则会被诸如页面权重、CSS 或 JavaScript 资源大小等性能瓶颈所影响。
这个指标不属于 PWM 的一部分,但它对于理解下面将要讲到的指标很有帮助。
所以需要其他一些指标来表示真实的内容绘制。
这是当用户看见一些“内容”元素被绘制在页面上的时间点。和白屏是不一样的,它可以是文本的首次出现,或者 SVG 的首次出现,或者 Canvas 的首次出现等等。
因此,用户可能会产生疑问,它正在运行吗? 页面是否在他(她)键入 URL 并按 enter 键后开始加载了呢?
继续看一下 Chromium,FCP 事件在文本(正在等待字体文件加载的文本不计算在内)、图片、Canvas 等元素绘制期间就已经被触发了。因此,FP 和 FCP 的时间差异可能从几毫秒到几秒不等。这个差别甚至可以从上面的图片中看出来。这就是为什么用一个指标来表示真实的首次内容绘制是有价值的。
你可以从这里阅读所有的规范说明。
FCP 指标如何对开发者产生价值?
如果首次内容绘制耗时太长,那么:
阅读 Ilya Grigorik 写的 High Performance Browser Networking 了解更多关于网络性能的问题,消除这些因素的影响。
这是指页面主要内容出现在屏幕上的时间点,因此,它有用吗?
主要内容是什么?
当
展示的时候。
但如果展示的是
则不计算在主要内容之内。
FMP = 最大布局变化时的绘制
基于 Chromium 的实现,这个绘制是使用 LayoutAnalyzer 进行计算的,它会收集所有的布局变化,当布局发生最大变化时得出时间。而这个时间就是 FMP。
你可以从这里阅读所有的规范说明。
FMP 指标如何对开发者产生帮助?
如果主要内容很久都没有展示出来,那么:
我不想重复太多已有的用来提升这些瓶颈的实践方法,给大家留出一些链接:
从这些文章里可以找到所有需要的信息。
当页面看上去“接近”加载完成,但浏览器还没有执行完所有脚本文件的状态。
这个指标意在估计应用对于用户输入的响应有多流畅。
但在深入研究前,我想通过解释一些术语以便大家在理解上同步。
长任务
浏览器底层将所有用户输入打包在一个任务里(UI 任务),并在主线程中将它们放到一个队列里。除此之外,浏览器还必须在页面上解析、编译和执行 JavaScript 代码(应用任务)。如果每个应用任务要耗费很长时间的话,那么用户输入任务就可能在其他任务结束前受到阻塞。因此它就会延迟与页面的交互,页面行为就会变得卡顿有延迟。
简单来说,长任务就是指耗时大于 50 毫秒的解析、编译和执行 JavaScript 代码块。
你可以从这里阅读所有的规范说明。
长任务 API 已经在 Chrome 里实现,并用作测量主线程的繁忙程度。
回到预计输入延迟,用户会假设页面响应很快,但如果主线程正忙于处理各个长任务,那么就会让用户不满意。对于应用来说,用户体验至关重要,可以从 Measure Performance with the RAIL Model 这篇文章里阅读关于这种类型的性能瓶颈如何进行性能提升。
交互 - 它可以使用了吗? 是的,这是当用户看见视觉上准备好的页面时提出的问题,他们希望能与页面产生交互。
首次交互发生需满足以下条件:
首次交互 - 这个指标可以拆分成两个指标,首次交互的时间(Time to First Interactive,TTFI)和首次持续交互的时间(Time to First Consistently Interactive,TTCI)。
拆分的原因在于:
TTCI
使用逆序分析,从追踪线的尾端开始看,发现页面加载活动保持了 5 秒的安静并且再无更多的长任务执行,得到了一段叫做安静窗口的时期。安静窗口之后及第一个长任务(从安静期结束后开始算)之前的时间就是TTCI。
TTFI
这个指标的定义和 TTCI 有一点不同。我们从头至尾来分析跟踪时间轴。在 FMP 发生后应该有 3 秒的安静窗口。这个时间已经足够说明页面对于用户来说是可交互的。但可能会有长任务在这个安静窗口期间或之后开始执行,它们可以被忽略。
长任务 - 距离 FMP 很远执行的任务,并由 250ms 的执行时间期间(信道大小)和在信道大小前后的 1 秒安静期分隔开来。这个示例任务有可能是第三方广告或者分析脚本。
有时长于 250 毫秒的“长任务”会对页面有严重的影响。
比如检测adblock
你可以从这里阅读所有的规范说明。
TTFI 和 TTCI 指标如何对开发者产生帮助?
当线程长时间处于视觉上准备好和首次交互中间忙碌状态时
这是其中一个最复杂的瓶颈,并且没有标准方法来修复这类型的问题。它是独立的,而且取决于应用的特定情况。Chrome 开发者工具有一系列文章帮助我们检测运行时的性能问题。
视觉上完成是通过页面截图来计算的,并使用速度指数算法来对那些截图进行像素分析。有时候测量是否视觉上完成也是一件棘手的事情。
如果页面里有会发生变化的图片如轮播图,那么获取正确的视觉上完成结果就可能有点挑战了。
速度指数本身表示视觉上完成结果的中值。速度指数的值越小,性能就越好。
视觉上 100% 完成是一个最终点,决定了用户对页面是否感到满意。这个时间也是用来回答问题 - 用户体验良好吗?
上述并不是所有的 PWM,但是最重要的一部分。上面的指标都增加了一些资料链接,帮助我们更好地提升它们,另外,我还想留出一些关于测量这些类型指标的工具链接:
P.S. 要获得所有这些指标的结果的话,我推荐使用 Lighthouse 或 pwmetrics。Calibre 和 WPT 都可以运行 Lighthouse,并可以通过扩展提供所有这些指标。
如果你想手动测量性能,有一个原生 API,叫 PerformanceObserver,它可以帮助你实现你的测量目标。
从规范说明里截取的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
const observer =
new PerformanceObserver(list => {
list
.getEntries()
// Get the values we are interested
in
.map(
({ name, entryType, startTime, duration }) => {
const obj = {
"Duration": duration,
"Entry Type": entryType,
"Name": name,
"Start Time": startTime,
};
return JSON.stringify(obj,
null,
2);
})
// Display them to the
console
.forEach(
console.log);
// maybe disconnect after processing the events.
observer.disconnect();
});
// retrieve buffered events
and subscribe to
new events
//
for Resource-Timing
and User-Timing
observer.observe({
entryTypes: [
"resource",
"mark",
"measure"],
buffered:
true
});
|
感谢所有工作人员,他们在规范说明、文章和工具上做了很出色的工作!