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

本案例研究介绍了利用 PageSpeed Insights (PSI)Chrome DevToolsscheduler.yield API 等 Google 工具,以便调试和改进 Trendyol 使用的 React 中的 INP 的逐步工作流程。

Gilberto Cocchi
Gilberto Cocchi

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

Trendyol 是一个电子商务平台,拥有约 3,000 万客户和 24 万卖家,这让我们成为土耳其首家估值超过 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 毫秒,属于“较差”范围。
2023 年 9 月 5 日起,Trendyol 的 INP 将由 PSI 提供。

为确保良好的响应速度,网站所有者应将 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 开发者工具中模拟移动性能时,在 Chrome 开发者工具中模拟 CPU 速度降低 4 倍后,系统发现主线程上需要耗时 700-900 毫秒的任务。如果主线程忙于处理其他任务的时间超过 50 毫秒,则可能无法及时响应用户输入,从而导致用户体验不佳。

一张屏幕截图,显示了 Chrome DevTools 中针对 Trendyol 的 PLP 进行的性能分析会话。描述的长任务运行 737.6 毫秒,是 Intersection Observer 回调的一部分。
Chrome DevTools 性能面板中 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 是一个不错的起点。确定存在问题的网页后,您可以使用 DevTools 进行深入研究,尝试重现问题。

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

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