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

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

任何电子商务网站的两个关键组成部分是商品详情页面 (PLP) 和商品详情页面 (PDP)。电子商务流量通常来自商品详情页面,无论是通过电子邮件广告系列、社交媒体还是广告带来的。因此,确保精选购物体验经过精心设计,以缩短购买所需的时间,至关重要。优先考虑用户体验质量对取得成功至关重要。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 DevTools 中模拟移动设备性能,并将 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 在 6 个月内第 75 百分位的 INP 的屏幕截图。6 个月后,Trendyol 的 INP 从近 1,400 毫秒下降到近 650 毫秒。
第 75 个百分位的 INP 改进随时间的变化情况。

总结

优化 INP 是一个复杂且迭代的过程,但通过清晰的工作流程,可以更轻松地完成。用于调试和改进网站 INP 的简单方法取决于您是否在收集自己的现场数据。如果您还没有开始,PSI 和 Lighthouse 是一个不错的起点。确定存在问题的网页后,您可以使用 DevTools 进行深入研究,尝试重现问题。

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

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