本案例研究介绍了利用 PageSpeed Insights (PSI)、Chrome 开发者工具和 scheduler.yield
API 等 Google 工具,在 React 中调试和改进 INP 的分步工作流程。Trendyol
任何电子商务网站的两个关键组成部分是商品详情页面 (PLP) 和商品详情页面 (PDP)。电子商务流量通常来自商品详情页面,无论是电子邮件广告系列、社交媒体还是广告。因此,确保精心设计 PLP 体验以缩短购买时间至关重要。优先考虑用户体验质量对于取得成功至关重要。《Milliseconds Make Millions》等研究出版物已指出,网页性能对消费者在线消费和与品牌互动的意愿有重大影响。
Trendyol 是一个拥有大约 3,000 万客户和 24 万个卖家的电子商务平台,这推动我们成为土耳其第一家估值超过 100 亿美元的企业,并成为全球顶级电子商务平台之一。
为了实现大规模提供最佳用户体验、同时保持内容灵活性并使用旧版 React 的目标,Trendyol 专注于将互动到下一次绘制 (INP) 作为一项关键指标进行改进。本案例研究介绍了 Trendyol 改善其 PLP 的 INP 过程:INP 降低 50% ,搜索结果业务指标提高 1% 。
Trendyol 的 INP 调查流程
INP 可以衡量网站对用户输入的响应能力。良好的 INP 表示浏览器能够快速可靠地响应所有用户输入并重新绘制页面,这是良好用户体验的关键组成部分。
Trendyol 改善其 PLP 的 INP 历程始于在做出任何改进之前对用户体验进行全面分析。根据 PSI 报告,PLP 的实际用户体验在移动设备上的 INP 为 963 毫秒,如下图所示。
![Trendyol 的 INP 根据 PageSpeed Insights 中的 CrUX 读取得出。截至 2023 年 9 月 5 日,Trendyol 的 INP 为 963 毫秒,属于“欠佳”范围。](https://web.developers.google.cn/static/case-studies/trendyol-inp/image/fig-1.png?authuser=6&hl=sr)
为了确保良好的响应能力,网站所有者应力求将 INP 控制在 200 毫秒以下,这意味着,当时 Trendyol 的 INP 处于“不佳”范围。
幸运的是,PSI 既提供了 Chrome 用户体验报告 (CrUX) 中所含网页的实测数据和详细的实验室诊断数据,Lighthouse 的 JavaScript 执行时间审核显示实验室数据表明,与页面上的其他脚本相比,search-result-v2
脚本占用主线程的时间更多。
![我们在 Lighthouse 中列出了 Trendyol 网站上长时间运行的任务的来源。耗时较长的任务的一个主要来源是在 Trendyol 的 PLP 上处理搜索结果的脚本。](https://web.developers.google.cn/static/case-studies/trendyol-inp/image/fig-2.png?authuser=6&hl=sr)
为了找出实际瓶颈,我们使用 Chrome 开发者工具中的性能面板来排查 PLP 体验问题并找出问题根源。在 Chrome 开发者工具中以 4 倍的 CPU 速度减慢来模拟移动设备性能,结果显示在主线程上执行一项时长为 700-900 毫秒的任务。如果主线程被其他任务占用超过 50 毫秒,它可能无法及时响应用户输入,导致用户体验不佳。
![Chrome 开发者工具中 Trendyol PLP 的性能分析会话屏幕截图。这个耗时较长的任务需要运行 737.6 毫秒,它属于 Intersection Observer 回调的一部分。](https://web.developers.google.cn/static/case-studies/trendyol-inp/image/fig-3.png?authuser=6&hl=sr)
耗时最长的任务是由对 React 组件内搜索结果脚本的 Intersection Observer API 回调产生的。这时,我们开始考虑将这个长任务分解为多个小任务,让浏览器有更多机会响应优先级较高的工作,包括用户互动。
事实证明,使用 setState
操作会在 Intersection Observer 回调内触发 React 重新渲染,但代价很高;这对于低端设备而言,会占用主线程过长时间,从而出现问题。
开发者曾使用一种方法将任务分解为多个较小的任务,该方法涉及 setTimeout
。我们使用这种方法将 setState
调用推迟到单独的任务中。虽然 setTimeout
允许推迟 JavaScript 的执行,但它无法控制优先级。这促使我们加入 scheduler.yield
源试用,以确保在让出主线程后继续执行我们的脚本:
/*
* Yielding method using scheduler.yield, falling back to setTimeout:
*/
async function yieldToMain() {
if('scheduler' in window && 'yield' in scheduler) {
return await scheduler.yield();
}
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
/*
* Yielding to the main thread before changing the state of the component:
*/
const observer = new IntersectionObserver((entries) => {
entries.forEach(handleIntersection);
const maxNumberOfEntries = Math.max(...this.intersectingEntries);
if (Number.isFinite(maxNumberOfEntries)) {
await this.yieldToMain();
this.setState({ count: maxNumberOfEntries });
}
}, { threshold: 0.5 });
向 PLP 代码添加这种让出方法可以改进 INP,因为主要的长任务被拆分为一系列较小的任务,这样可以比以往更快地完成优先级较高的工作,例如用户互动和后续渲染工作。
![Chrome 开发者工具中 Trendyol PLP 的性能分析会话屏幕截图。之前运行 737.6 毫秒的耗时较长任务现在被拆分为几个较小的任务。](https://web.developers.google.cn/static/case-studies/trendyol-inp/image/fig-4.png?authuser=6&hl=sr)
请注意,Trendyol 通过 PuzzleJs 框架通过 React v16.9.0 实现微前端架构。使用 React 18 也可以实现相同的性能,但出于多种原因,Trendyol 目前无法升级。
业务成效
为了衡量已实施的 INP 改进的影响,我们运行了 A/B 测试,以了解业务指标受到的影响。总体而言,我们对 PLP 的更改带来了显著的改进,其中包括 INP 降低 50%,每次用户会话从商品详情页面到商品详情页面的点击率提升 1%。在下图中,您可以看到 PLP 上的 INP 随时间的改进情况:
![Trendyol 第 75 百分位的 INP 在 6 个月内的屏幕截图。六个月结束时,Trendyol 的 INP 从近 1,400 毫秒减少到近 650 毫秒。](https://web.developers.google.cn/static/case-studies/trendyol-inp/image/fig-5.png?authuser=6&hl=sr)
总结
优化 INP 是一个复杂的迭代过程,但通过明确的工作流程,您可以更轻松地进行优化。调试和改进网站 INP 的简单方法取决于您是否在收集自己的现场数据。否则,不妨从 PSI 和 Lighthouse 入手。发现存在问题的网页后,您可以使用开发者工具进行更深入的分析,以尝试重现问题。
不时让出主线程,让浏览器有更多机会执行紧急工作,这将使您的网站响应更快,从而确保客户拥有更好的用户体验。新的调度 API(如 scheduler.yield()
)可简化此任务。
特别感谢 Google 的 Jeremy Wagner、Barry Pollard 和 Houssein Djirdeh 以及 Trendyol 的工程团队对这项工作做出的贡献。