Taboola 如何利用 Long Animation Frames API (LoAF) 并采用智能收益策略,在不影响广告效果的情况下提高发布商的网站响应速度。
Interaction to Next Paint (INP) 是一个指标,用于评估网站对用户输入的响应能力。INP 衡量的是从用户开始互动(例如点击、点按或输入)到产生的视觉反馈所用的时间。INP 将于 2024 年 3 月取代 First Input Delay (FID) 作为 Core Web Vitals 指标。
Taboola 是全球领先的内容发现平台,每秒在开放网络上为 50 万次推荐提供支持。借助这些建议,Taboola 的 9,000 家独家发布商合作伙伴能够从受众群体中获利并吸引受众群体。发布商使用 JavaScript 在其网页上呈现推荐内容。
由于第三方 JavaScript 会影响网页快速响应用户输入的能力,因此,Taboola 投入了大量资源来缩减其 JavaScript 文件大小并缩短执行时间。Taboola 一直在重新设计整个渲染引擎,并直接使用未进行抽象化处理的浏览器 API,以尽量减少其对 INP 的影响。
本案例研究介绍了 Taboola 如何使用新的 Long Animation Frames (LoAF) API 来衡量其对实际页面响应速度的影响,从而改进 INP 的历程,以及后续如何应用特定优化措施来提升用户体验。
TBT 作为 INP 的替代指标
总阻塞时间 (TBT) 是一项基于实验室的指标,用于标识主线程在什么位置被阻塞的时间足够长,可能会影响网页响应速度。衡量响应能力的实测指标(如 INP)可能会受到 TBT 高的影响。Annie Sullivan 对移动设备上的 TBT 与 INP 之间的关联进行的研究表明,尽可能缩短主线程阻塞时间后,网站更有可能获得良好的 INP 得分。
这种相关性,再加上 Taboola 发布商对 TBT 高的担忧,使得 Taboola 将注意力集中在尽可能减少对该指标的贡献上。
![Lighthouse 针对阻塞主线程时间的审核的屏幕截图。主线程被多个脚本总共阻塞了 2,630 毫秒,而第三方 JavaScript 则贡献了 712 毫秒。Taboola 的 RELEASE.js 脚本负责大部分第三方阻塞时间,即 691 毫秒。](https://web.developers.google.cn/static/case-studies/taboola-inp/image/fig-1.png?authuser=8&hl=hu)
RELEASE.js
等脚本会将主线程阻塞 691 毫秒。
Taboola 使用 TBT 作为 INP 的代理指标,开始监控和优化 JavaScript 执行时间,以限制其对核心网页指标的潜在影响。他们首先采取了以下措施:
- 使用 Long Tasks API 找出并优化现场存在问题的脚本。
- 通过使用 PageSpeed Insights API 评估每天 10,000 到 15,000 个网址,从而估算 TBT 贡献量。
不过,Taboola 注意到,使用这些工具分析 TBT 存在一些局限性:
- Long Tasks API 无法将任务归因于源网域或特定脚本,因此更难识别长任务的来源。
- Long Tasks API 只能识别长时间运行的任务,而非可能导致渲染延迟的任务和布局更改的组合。
为了克服这些挑战,Taboola 加入了 Long Animation Frames (LoAF) API 源试用,以便更好地了解它对用户输入响应能力的真实影响。源试用是指用户能够使用新功能或实验性功能,让开发者能够测试新功能,并允许用户限时试用这些新功能。
请务必强调,此次挑战中最困难的方面是如何成功改进 INP,而不影响任何广告 KPI(关键绩效指标)或导致发布商资源延迟。
使用 LoAF 评估 INP 的影响
如果渲染更新延迟超过 50 毫秒,就会出现较长的动画帧。通过找出导致界面更新缓慢的原因(而不仅仅是冗长任务),Taboola 能够分析其对实际应用页面响应速度的影响。观察 LoAF 让 Taboola 可以:
- 将条目归因于特定的 Taboola 任务。
- 在将特定功能部署到生产环境之前,观察特定功能存在的性能问题。
- 收集汇总数据以比较 A/B 测试中的不同代码版本,并报告关键成效指标。
以下 JavaScript 是在生产环境中使用的简化版本,用于收集 LoAF,以分离 Taboola 的影响。
function loafEntryAnalysis (entry) {
if (entry.blockingDuration === 0) {
return;
}
let taboolaIsMajor = false;
const hasInteraction = entry.firstUIEventTimestamp > 0;
let taboolaDuration = 0;
const nonTaboolaLoafReport = {};
const taboolaLoafReport = {};
entry.scripts.forEach((script) => {
const taboolaScriptBlockingDuration = handleLongAnimationFrameScript(script, taboolaLoafReport, nonTaboolaLoafReport);
taboolaDuration += taboolaScriptBlockingDuration;
if (taboolaScriptBlockingDuration > 0 || taboolaDuration > entry.duration / 2) {
taboolaIsMajor = true;
}
});
generateToboolaLoafReport(taboolaLoafReport, nonTaboolaLoafReport, hasInteraction, taboolaIsMajor);
if (hasInteraction) {
const global = _longAnimationFramesReport.global;
global.inpBlockingDuration = Math.max(global.inpBlockingDuration, entry.blockingDuration);
if (taboolaIsMajor) {
global.taboolaInpBlockingDuration = Math.max(global.taboolaInpBlockingDuration, entry.blockingDuration);
}
}
}
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
loafEntryAnalysis(entry);
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
- Taboola 使用
loafEntryAnalysis
函数可以识别出它在哪个条目中起到主要作用。 - 如果来自 Taboola 的脚本总时长超过一半或是由 Taboola 脚本的运行时间超过 50 毫秒,则系统认为 Taboola 是主要影响因素。
- 如果用户互动因长动画帧而延迟,系统会生成
firstUIEventTimeStamp
。最长的阻塞持续时间会被视为总体 INP 得分。我们还可以确定 Taboola 何时触发了firstUIEventTimeStamp
来计算 Taboola INP 得分。
通过 LoAF 收集的数据帮助 Taboola 创建了以下归因表,确定它可以在哪些方面应用创收机会。
TRECS Engine:新的收益策略
除了使用 LoAF 来更好地了解脚本优化机会之外,Taboola 还一直在重新设计整个渲染引擎,以大幅缩短 JavaScript 的执行和阻塞时间。
TRECS (Taboola Recommendations 可扩展客户端服务) 可维护客户端渲染和发布商当前的 JS 代码,同时减少加载 Taboola 的建议所需的必需文件的数量和大小。
在使用 LoAF 识别出渲染阻塞任务后,“Performance Fader”可以先将这些任务分解,然后再使用 scheduler.postTask()
让出主线程。这种设计可确保,无论任何现有任务可能占用主线程,都能尽快执行面向用户的关键工作(例如渲染更新)。
以下是“Performance Fader”任务运行程序的 JS 代码段:
/**
* Send a task to run using the Fader. The task will run using the browser Scheduler, by the configuration settings, or immediately.
* @param task
* @param isBlocker
*/
function sendTaskToFader (task, isBlocker = true) {
const publisherFaderChoice = fillOptimizationGlobals(); // Loading publisher choice
const applyYielding = publisherFaderChoice === OptimizationFaderType.Responsiveness;
if (applyYielding) {
return runAsPostTask(task, isBlocker);
}
return runImmediately(task);
}
/**
* Yielding method using scheduler.postTask and falling back to setTimeout when it's not availabe based on the publisher choice
*/
function runAsPostTask (task, isBlocker = true) {
if ('scheduler' in window && 'postTask' in scheduler) {
const priority = isBlocker ? 'user-blocking': 'background';
return window?.scheduler?.postTask(task, { priority });
}
const publisherChoiceEnableFallback = fillPublisherChoices();
if (publisherChoiceEnableFallback) {
return new Promise(resolve => {
window.setTimeout(() => {
resolve(task());
}, 0);
});
}
return runImmediately(task);
}
sendTaskToFader
函数
- 使用
runAsPostTask
,后者会在后台使用scheduler.postTask()
(如果 API 可用),或回退到setTimeout
。 - 此函数会将函数调用封装在导致长动画帧和 INP 的代码部分中。它会将这些代码部分拆分为较短的任务,从而减少 INP。
业务指标
得益于 LoAF,Taboola 能够更好地了解其对 INP 的影响。该工具还突出显示了可用作新 TRECS 引擎一部分的脚本优化机会。
为了确定 TRECS 和 Performance Fader 的影响,Taboola 对一组发行商合作伙伴进行了一项 A/B 测试,以对照现有引擎来衡量 INP,而无需生成任何脚本。
下表显示了 Taboola 网络中四个匿名发布商的第 75 个百分位的 INP 结果(以毫秒为单位)。
幸运的是,在测试面板启用 TRECS 和 Performance Fader 后,广告点击率和每千次展示收入 (RPM) 等业务指标并未受到负面影响。随着对 INP 的积极改进,广告 KPI 没有达到预期的效果,Taboola 将会逐步提升发布商对其产品的看法。
之前针对同一客户运行的另一个 Lighthouse 显示,使用新引擎时,Taboola 显著缩短了主线程阻塞时间。
![屏幕截图:采用新的 TRECS 和 Performance Fader 引擎以缩短主线程阻塞时间后,对主线程阻塞时间进行的 Lighthouse 审核。审计时间缩短至仅 206 毫秒,与优化前的 712 毫秒相比。](https://web.developers.google.cn/static/case-studies/taboola-inp/image/fig-2.png?authuser=8&hl=hu)
RELEASE.js
等脚本将 TBT 缩短了 485 毫秒 (-70%)。
这表明,使用 LoAF 找出 INP 的原因,并通过 Performance Fader 部署后续产出技术,可让 Taboola 的合作伙伴在广告和页面性能方面取得最大成功。
总结
优化 INP 是一个复杂的过程,尤其是在合作伙伴网站上使用第三方脚本的情况下。在开始优化之前,将 INP 归因于特定脚本可以消除任何猜测,并消除对其他网站性能指标的潜在损害。事实证明,LoAF API 是一款非常有价值的工具,可用于识别和解决 INP 问题(尤其是对于嵌入的第三方)。通过它,他们可以精确找出具体的 SDK 改进机会,同时消除对网页上其他技术的干扰。
结合有效的收益策略(例如使用 scheduler.postTask()
)时,LoAF 可帮助您观察和了解网页响应速度不佳的原因,进而为您提供改进网站 INP 所需的信息。
特别感谢 Google 的 Gilberto Cocchi、Noam Rosenthal 和 Rick Viscomi,以及 Taboola 工程和产品团队的 Dedi Hakak、Anat Dagan 和 Omri Ariav,对这项工作做出的贡献。