预提取如何帮助 Terra 将广告点击率提高 30% 并加快 Largest Contentful Paint。

预提取资源可缩短网页加载时间并提升业务指标。

Guilherme Moser de Souza
Guilherme Moser de Souza

预提取是一项技术,用于通过下载近期可能会需要的资源(甚至整个网页)来加快网页加载速度。研究表明,加载时间越短,转化率越高,用户体验就越好。

Terra 是巴西最大的内容门户之一,提供娱乐、新闻和体育内容,每月的唯一身份访问者数量超过 6300 万。我们与 Terra 的工程团队合作,在其网站的特定版块上使用预提取技术,从而缩短报道的加载时间。

本案例研究介绍了 Terra 实施历程后,移动设备上的广告点击率 (CTR) 提高了 11%,桌面广告点击率 (CTR) 提高了 30%,Largest Contentful Paint (LCP) 时间减少了 50%。

预提取策略

预提取已经存在一段时间了,但请务必小心使用,因为预提取会消耗额外的带宽,用于并非当下必需的资源。应谨慎应用此技术,以避免不必要的数据使用。对于 Terra,如果满足以下条件,预提取报道:

  • 指向预提取报道的链接的可见性:Terra 使用 Intersection Observer API 检测包含要预提取的报道的版块的可见度。
  • 增加流量消耗的有利条件:如前所述,预提取是一种推测性性能改进,会消耗额外的数据,但并不是在所有情况下都可取到理想的结果。为了降低浪费带宽的可能性,Terra 结合使用 Network Information API 以及 Device Memory API 来确定是否提取下一篇文章。仅当满足以下条件时,Terra 才会获取下一篇文章:
    • 连接速度至少为 3G,设备至少具有 4GB 内存,
    • 或者设备搭载的是 iOS 系统
  • CPU 空闲:最后,Terra 将使用 requestIdleCallback 来检查 CPU 是否处于空闲状态并执行额外的工作,该回调接受回调以处理,以便在主线程空闲时处理,或以特定的(可选)截止时间为准。

遵循这些条件可确保 Terra 仅在必要时提取数据,从而节省带宽和电池续航时间,并最大限度地减少最终未使用的预提取的影响。

满足这些条件后,Terra 会预提取“相关内容”和“为您推荐”部分(下方以蓝色突出显示的内容)中的文章。

Terra 网站上两个部分预提取链接的屏幕截图。在左侧,“相关内容”部分突出显示,而右侧“推荐”部分突出显示。

业务影响

为了衡量此技术的影响,Terra 首先在文章页面的“相关内容”部分推出了此功能。UTM 代码帮助他们区分了预提取的报道和未预提取的报道,以便进行比较。在成功进行 A/B 测试两周后,Terra 决定将预提取功能添加到“为你推荐”部分。

预提取报道后,广告指标总体上有所提高,且 LCP 和首字节时间 (TTFB) 时间有所降低:

指标 移动设备 桌面设备
广告点击率 +11% +30%
广告可见度 提升了 10.5% +6%
LCP -51% -73%
TTFB -83% -84%

预提取时要谨慎,它可以大幅缩短网页加载时间,增加广告指标,并缩短 LCP 时间。

技术详情

预提取可通过使用 rel=prefetchrel=preload 等资源提示、通过 quicklinkGuess.js 等库或使用较新的 Speculation Rules API 来实现。Terra 选择将低优先级 fetch API 与 Intersection Observer 实例结合使用,以达到此目的。Terra 之所以做出这一选择,是因为它支持 Safari,但 Safari 尚不支持 rel=prefetch 或 Speculation Rules API 等其他预提取方法,而且功能齐全的 JavaScript 库也能满足 Terra 的需求。

以下 JavaScript 大致相当于 Terra 使用的代码:

function prefetch(nodeLists) {
  // Exclude slow ECTs < 3g
  if (navigator.connection &&
    (navigator.connection.effectiveType === 'slow-2g'
      || navigator.connection.effectiveType === '2g')
  ) {
    return;
  }

  // Exclude low end device which is device with memory <= 2GB
  if (navigator.deviceMemory && navigator.deviceMemory <= 2) {
    return;
  }

  const fetchLinkList = {};

  const observer = new IntersectionObserver(function (entries) {
    entries.forEach(function (entry) {
      if (entry.isIntersecting) {
        if (!fetchLinkList[entry.target.href]) {
          fetchLinkList[entry.target.href] = true;

          fetch(entry.target, {
            priority: 'low'
          });
        }

        observer.unobserve(entry = entry.target);
      }
    });
  });
}

const idleCallback = window.requestIdleCallback || function (cb) {
  let start = Date.now();

  return setTimeout(function () {
    cb({
      didTimeout: false,
      timeRemaining: function () {
        return Math.max(0, 50 - (Date.now() - start));
      }
    });
  }, 1);
}

idleCallback(function () {
  prefetch(nodeLists)
})
  • prefetch 函数首先检查最低连接质量和设备内存,然后再启动预提取。
  • 然后,它使用 IntersectionObserver 监控元素何时在视口中变得可见,随后将网址添加到列表中以进行预提取。
  • 预提取进程使用 requestIdleCallback 进行调度,目的是在主线程空闲时执行 prefetch 函数。

总结

谨慎使用预提取功能时,可以显著缩短未来导航请求的加载时间,从而减少用户体验历程中的阻碍并提高互动度。预提取会导致加载可能不会被使用的额外字节,因此 Terra 采取额外的步骤,仅在良好的网络条件下以及能够获得此信息的设备上进行预提取。

特别感谢 Terra 工程团队的 Gilberto Cocchi、Harry Theodoulou、Miguel Carlos Martínez Díaz、Barry Pollard、Jeremy Wagner、Leonardo Bellini 和 Lucca Paradeda 对这项工作所做的贡献。