第一章 Lighhthouse 的数据指标
第二章 以用户为中心的性能优化
浏览器通过将任务添加到主线程上的队列等待逐个执行来响应用户输入。 浏览器执行应用的 JavaScript 时也会这样做,因此从这个角度看,浏览器为单线程。
在某些情况下,运行这些任务可能要花费较长时间,如果确实如此,主线程就会遭到阻止,而队列中的所有其他任务都必须等待。

对于用户而言,任务耗时较长表现为滞后或卡顿,而这也是目前网页不良体验的主要根源。
Long Tasks API 可以将任何耗时超过 50 毫秒的任务标示为可能存在问题,并向应用开发者显示这些任务。 选择 50 毫秒的时间是为了让应用满足在 100 毫秒内响应用户输入的 RAIL 指导原则。
Speed index
优化内容效率
优化关键渲染路径
RAIL 模型评估
Rail 是一种以用户往往为中心的性能模型
每个网络应用都有与起生命周期相关的四个不同的方向,并且这些方向以不同的方式影响着性能

以用户未中心:以用户未未中心不说让我们的网站在任何特定的设备都能运行很快,而是使用户满意
立即响应用户: 在100ms 内确定用户的输入
设置动画或者滚动,在10ms 内生成帧
最大限度增加主线程的空闲时间
持续吸引用户;在1000ms呈现交互内容

以用户为中心
让用户成为您的性能工作的中心。用户花在网站上的大多数时间不是等待加载,而是在使用时等待响应。了解用户如何评价性能延迟:

响应:在 100 毫秒以内响应
在用户注意到滞后之前您有 100 毫秒的时间可以响应用户输入。这适用于大多数输入,不管他们是在点击按钮、切换表单控件还是启动动画。但不适用于触摸拖动或滚动。
如果您未响应,操作与反应之间的连接就会中断。用户会注意到。
尽管很明显应立即响应用户的操作,但这并不总是正确的做法。使用此 100 毫秒窗口执行其他开销大的工作,但需要谨慎,以免妨碍用户。如果可能,请在后台执行工作。
对于需要超过 500 毫秒才能完成的操作,请始终提供反馈。
动画:在 10 毫秒内生成一帧
动画不只是奇特的 UI 效果。例如,滚动和触摸拖动就是动画类型。
如果动画帧率发生变化,您的用户确实会注意到。您的目标就是每秒生成 60 帧,每一帧必须完成以下所有步骤:

从纯粹的数学角度而言,每帧的预算约为 16 毫秒(1000 毫秒 / 60 帧 = 16.66 毫秒/帧)。 但因为浏览器需要花费时间将新帧绘制到屏幕上,只有 10 毫秒来执行代码
在像动画一样的高压点中,关键是不论能不能做,什么都不要做,做最少的工作。 如果可能,请利用 100 毫秒响应预先计算开销大的工作,这样您就可以尽可能增加实现 60fps 的可能性。
跟踪耗时较长的任务
上文提到,耗时较长的任务通常会带来某种负面的用户体验(例如, 事件处理程序运行缓慢,或者掉帧)。 您最好了解发生这种情况的频率,以设法尽量减少这种情况。
要在 JavaScript 中检测耗时较长的任务,请创建新的 PerformanceObserver,并观察类型为 longtask 的条目。 耗时较长的任务条目的一个有点是包含提供方属性,有助于您更轻松地追查导致出现耗时较长任务的代码:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
ga(‘send’, ‘event’, {
eventCategory:’Performance Metrics’,
eventAction: ‘longtask’,
eventValue:Math.round(entry.startTime + entry.duration),
eventLabel:JSON.stringify(entry.attribution),
});
}
});
observer.observe({entryTypes: [‘longtask’]});
提供方属性会指出导致耗时较长任务的帧上下文,这有助于您确定问题是否由第三方 iframe 脚本所致。 未来版本的规范计划添加更多详细信息并公开脚本网址、行号和列号,这有助于确定速度缓慢问题是否由您自己的脚本所致。
跟踪输入延迟
阻塞主线程的耗时较长任务可能会导致事件侦听器无法及时执行。 RAIL 性能模型指出,为提供流畅的界面体验,界面应在用户执行输入后的 100 毫秒内作出响应,若非如此,请务必探查原因。
若要在代码中检测输入延迟,您可将事件时间戳与当前时间作比较,如果两者相差超过 100 毫秒,您可以并应该进行报告。
const subscribeBtn = document.querySelector(‘#subscribe’);
subscribeBtn.addEventListener(‘click’, (event) => {
// Event listener logic goes here…
const lag = performance.now() - event.timeStamp;
if (lag > 100) {
ga(‘send’, ‘event’, {
eventCategory:’Performance Metric’
eventAction: ‘input-latency’,
eventLabel: ‘#subscribe:click’,
eventValue:Math.round(lag),
nonInteraction: true,
});
}
});
由于事件延迟通常是由耗时较长的任务所致,因此您可将事件延迟检测逻辑与耗时较长任务检测逻辑相结合:如果某个耗时较长的任务在 event.timeStamp 所示的时间阻塞主线程,您也可以报告该耗时较长任务的提供方值。 如此,您即可在负面性能体验与导致该体验的代码之间建立明确的联系。
虽然这种方法并不完美(不会处理之后在传播阶段耗时较长的事件侦听器,也不适用于不在主线程中运行的滚动或合成动画),但确实是良好的开端,让您能更好地了解运行时间较长的 JavaScript 代码对用户体验产生影响的频率。
移动设备
百分位 TTI(秒)
50% 3.9
75% 8.0
90% 12.6
按移动设备与桌面设备划分结果,并以分布图的方式分析数据,可让您迅速了解实际用户的体验。 例如,通过上表,我很容易便可发现,对于此应用,10% 的移动用户需等待 12 秒以上才能进行交互!
性能对业务的影响
在分析工具中跟踪性能有一项巨大优势,即您之后可以利用该数据来分析性能对业务的影响。
如果您在分析工具中跟踪目标达成情况或电子商务转化情况,则可通过创建报告来探查两者与应用性能指标之间的关联。 例如:
· 体验到更快交互速度的用户是否会购买更多商品?
· 如果用户在结账流程中遇到较多耗时较长的任务,其离开率是否较高?
如果发现存在关联,即可轻松建立性能至关重要且应该优先考虑的商业案例。
加载放弃
我们知道,用户经常会因为页面加载时间过长而选择离开。 不幸的是,这意味着我们的所有性能指标都存在幸存者偏差,即数据不包括未等待页面加载完成的用户的加载指标(这很可能意味着数量过低)。
虽然您无法获悉如果这类用户逗留所产生的指标,但可以跟踪发生这种情况的频率以及每位用户逗留的时长。
使用 Google Analytics 完成此任务比较棘手,因为 analytics.js 库通常是以异步方式加载,而且可能在用户决定离开前尚不可用。 但是,您无需等待 analytics.js 加载完成再将数据发送到 Google Analytics。 您可以直接通过 Measurement Protocol 发送数据。
以下代码为 visibilitychange 事件添加侦听器(该事件在页面卸载或进入后台时触发),并在该事件触发时发送 performance.now() 值。
优化性能以及避免性能下降
定义以用户为中心的指标的好处在于,您优化这些指标时,用户体验必然也会同时改善。
改善性能最简单的一种方法是,直接减少发送到客户端的 JavaScript 代码,但如果无法缩减代码长度,则务必要思考如何提供 JavaScript。
优化 FP/FCP
从文档的
优化 FMP/TTI
确定页面上最关键的界面元素(主角元素)之后,应确保初始脚本加载仅包含渲染这些元素并使其可交互所需的代码。
初始 JavaScript 软件包中所包含的任何与主角元素无关的代码都会延长可交互时间。 没有理由强迫用户设备下载并解析当前不需要的 JavaScript 代码。
应该尽可能缩短 FMP 与 TTI 之间的时间, 如果无法最大限度缩短此时间,界面绝对有必要明确指出页面尚不可交互。
避免出现耗时较长的任务
拆分代码并按照优先顺序排列要加载的代码,不仅可以缩短页面可交互时间,还可以减少耗时较长的任务,然后即有希望减少输入延迟及慢速帧。
除了将代码拆分为多个单独的文件之外,您还可将大型同步代码块拆分为较小的块,以便以异步方式执行,或者推迟到下一空闲点。 以异步方式在较小的块中执行此逻辑,可在主线程中留出空间,供浏览器响应用户输入。
最后,您应确保测试第三方代码,并对任何低速运行的代码追责。 产生大量耗时较长任务的第三方广告或跟踪脚本对您业务的伤害大于帮助。
避免性能下降
本文重点强调的是针对实际用户的性能测量,虽然 RUM 数据确实是十分重要的性能数据,但实验室数据对于在发布新功能前确保应用性能良好(而且不会下降)而言仍然十分关键。 实验室测试非常适合用于检测性能是否下降,因为这些测试是在受控环境中运行,出现随机变化的可能性远低于 RUM 测试。
Lighthouse 和 Web Page Test 等工具可以集成到持续集成服务器中,而且您可以编写相应的测试,以在关键指标退化或下降到低于特定阈值时将构件判定为失败。
对于已发布的代码,您可以添加自定义提醒,以通知您负面性能事件的发生率是否意外突增。 例如,如果第三方发布其某个服务的新版本,而您的用户突然开始看到大量新增的耗时较长任务,就代表出现这种情况。
为成功避免性能下降,您必须在实验室和实际运行环境中针对发行的每个新功能进行性能测试。

【参考】
1、User-centric performance metrics by Philip Walton