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

本案例研究描述了 Trendyol 如何通过利用 Google 工具(如 PageSpeed Insights (PSI)Chrome DevToolsscheduler.yield API)调试和改进 React 中 INP 的逐步工作流程。

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

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

为了实现大规模提供最佳用户体验的目标,同时保持内容的灵活性并使用旧版本的 React,Trendyol 专注于将“交互到下次绘制”(INP) 作为改进的关键指标。本案例研究描述了 Trendyol 改进其 PLP 上 INP 的历程,最终使 INP 降低了 50%,搜索结果业务指标提升了 1%。

Trendyol 的 INP 调查流程

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

Trendyol 改进其 PLP 上 INP 的历程始于在进行任何改进之前对用户体验进行全面分析。根据 PSI 报告,PLP 的真实用户体验在移动设备上的 INP 为 963 毫秒,如下图所示。

Trendyol's INP according to the CrUX readout in PageSpeed Insights. Trendyol's INP as of September 5th, 2023 was 963 milliseconds, which is in the 'poor' range.
截至 2023 年 9 月 5 日,PSI 报告的 Trendyol INP。

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

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

A readout of sources of long tasks in Lighthouse for the Trendyol website. One major source of long tasks is a script that handles search results on Trendyol's PLP.
截至 2023 年 9 月 5 日,PSI 报告的 Lighthouse Trendyol JavaScript 执行时间审核。

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

A screenshot of a performance profiling session in Chrome DevTools for Trendyol's PLP. The long task depicted runs for 737.6 milliseconds, and is part of an Intersection Observer callback.
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 得到了改善,因为主要的长任务已被分解为一系列较小的任务,这使得更高优先级的工作(例如用户交互和后续渲染工作)能够比原本更快地发生。

A screenshot of a performance profiling session in Chrome DevTools for Trendyol's PLP. The long task that was previously running for 737.6 milliseconds is now split up into several smaller tasks.
任务分解为较小的任务。

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

业务成果

为了衡量实施 INP 改进的影响,我们进行了 A/B 测试,以查看业务指标如何受到影响。总体而言,我们对 PLP 的更改带来了显着的改进,包括 INP 降低了 50%,以及每个用户会话从列表页到商品详情页的点击率提升了 1%。在下图中,您可以看到 INP 如何随时间推移在 PLP 上得到改善

A screenshot of Trendyol's 75th percentile INP over the course of six months. By the end of the six months, Trendyol's INP decreased to nearly 650 milliseconds from nearly 1,400 milliseconds.
第 75 百分位数 INP 随时间推移的改进。

结论

优化 INP 是一个复杂且迭代的过程,但通过清晰的工作流程可以使其更容易。调试和改进网站 INP 的简单方法取决于您是否正在收集自己的字段数据。如果您没有收集,PSI 和 Lighthouse 是一个好的起点。一旦您确定了有问题页面,您可以使用 DevTools 深入挖掘以尝试重现问题。

不时地让步于主线程,让浏览器有更多机会执行紧急工作,这将使您的网站更具响应性,确保您的客户获得更好的用户体验。较新的调度 API(如 scheduler.yield())使此任务更容易。

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