Trendyol 如何将 INP 降低 50%,使点击率提高 1%

本案例研究介绍了 Trendyol 使用 PageSpeed Insights (PSI)Chrome 开发者工具scheduler.yield API 等 Google 工具,在 React 中调试和改进 INP 的分步工作流程。

Gilberto Cocchi
Gilberto Cocchi

任何电子商务网站的两个关键组成部分是商品详情页面 (PLP) 和商品详情页面 (PDP)。电子商务流量通常来自商品详情页面,无论是通过电子邮件广告系列、社交媒体还是广告都是如此。因此,确保精选购物体验经过精心设计,以缩短购买所需的时间,至关重要。优先考虑用户体验质量对取得成功至关重要。Milliseconds Make Millions 等研究出版物已揭示网页性能对消费者愿意在线消费和与品牌互动的影响力。

Trendyol 是一个电子商务平台,拥有约 3000 万客户和 240,000 个卖家,这让我们成为土耳其第一家估值超过 100 亿美元的企业,并且成为全球顶级电子商务平台之一。

为了实现其目标,即在保持内容灵活性并使用较低版本的 React 的同时,大规模提供尽可能出色的用户体验,Trendyol 将 Interaction to Next Paint (INP) 作为一个关键指标进行改进。此案例研究介绍了 Trendyol 如何改进其 PLP 的 INP,从而将 INP 降低了 50%,并将搜索结果业务指标提高了 1%

Trendyol 的 INP 调查流程

INP 用于衡量网站对用户输入的响应情况。良好的 INP 表示浏览器能够快速可靠地响应所有用户输入并重新绘制网页,这是良好用户体验的关键组成部分。

在开始改进 PLP 上的 INP 之前,Trendyol 先对用户体验进行了深入分析。根据 PSI 报告,PLP 的实际用户体验的 INP 为 963 毫秒(在移动设备上),如下图所示。

根据 PageSpeed Insights 中的 CrUX 读数,Trendyol 的 INP。截至 2023 年 9 月 5 日,Trendyol 的 INP 为 963 毫秒,属于“较差”范围。
Trendyol 的 INP 来自 PSI,截至 2023 年 9 月 5 日。

为了确保良好的响应能力,网站所有者应力求将 INP 控制在 200 毫秒以下,这意味着,当时 Trendyol 的 INP 在“较差”范围内。

幸运的是,PSI 既提供 Chrome 用户体验报告 (CrUX) 中包含的网页的实测数据,也提供详细的实验室诊断数据。查看实验室数据后,Lighthouse 的 JavaScript 执行时间审核结果表明,search-result-v2 脚本占用主线程的时间比页面上的其他脚本要长。

针对 Trendyol 网站的 Lighthouse 中长任务来源的读数。处理时间较长的任务的一个主要来源是处理 Trendyol PLP 上执行搜索结果的脚本。
PSI 在 2023 年 9 月 5 日对 Trendyol 的 JavaScript 执行时间进行了 Lighthouse 审核。

为了找出实际瓶颈,我们使用了 Chrome 开发者工具中的性能面板来排查 PLP 体验问题并找出问题根源。在 Chrome DevTools 中模拟移动设备性能,并将 CPU 速度降低 4 倍,发现主线程上有 700-900 毫秒的长任务。如果主线程被其他任务占用的时间超过 50 毫秒,则可能无法及时响应用户输入,从而导致用户体验不佳。

一张屏幕截图,显示了 Chrome DevTools 中针对 Trendyol 的 PLP 进行的性能分析会话。所示的长时间运行的任务的运行时间为 737.6 毫秒,是 Intersection Observer 回调的一部分。
在 Chrome 开发者工具的性能面板中,对 Trendyol PLP 执行耗时较长任务的性能分析器。

最长任务是由 React 组件内搜索结果脚本上的 Intersection Observer API 回调引起的。此时,我们开始考虑将该长任务拆分成小块,以便浏览器有更多机会响应更高优先级的工作(包括用户互动)。

事实证明,使用在 Intersection Observer 回调中触发 React 重新渲染的 setState 操作会产生高开销,这可能会导致低端设备占用主线程的时间过长,从而出现问题。

开发者用于将任务拆分为较小的任务涉及 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 DevTools 中针对 Trendyol 的 PLP 进行的性能分析会话。之前运行时间为 737.6 毫秒的长任务现在已拆分为多个较小的任务。
任务拆分为更小的任务。

请注意,Trendyol 使用 PuzzleJs 框架使用 React v16.9.0 实现微前端架构。使用 React 18 可以实现相同的性能,但出于多种原因,Trendyol 目前无法升级。

业务成效

为了衡量实施的 INP 改进的影响,我们进行了一项 A/B 测试,以了解业务指标受到了怎样的影响。总体而言,我们对 PLP 所做的更改带来了显著的改进,包括每次用户会话的 INP 降低了 50%,从商品详情页到商品详情页的点击率提高了 1%。在下图中,您可以看到 INP 在 PLP 方面的改进情况随时间的推移:

Trendyol 第 75 个百分位 INP 6 个月内的屏幕截图。6 个月后,Trendyol 的 INP 从近 1,400 毫秒下降到近 650 毫秒。
第 75 个百分位的 INP 改进随时间的变化情况。

总结

优化 INP 是一个复杂的迭代过程,但有了明确的工作流程可以简化这一过程。用于调试和改进网站 INP 的简单方法取决于您是否在收集自己的现场数据。如果您还没有开始,PSI 和 Lighthouse 是一个不错的起点。确定存在问题的网页后,您可以使用开发者工具进行更深入的挖掘,以尝试重现问题。

不时让出主线程,让浏览器有更多机会执行紧急工作,这将使您的网站响应速度更快,从而确保客户获得更好的用户体验。scheduler.yield() 等较新的调度 API 可简化此任务。

特别感谢 Google 的 Jeremy Wagner、Barry Pollard 和 Houssein Djirdeh,以及 Trendyol 的工程团队对本工作的贡献。