用于管理第三方库的 Next.js 软件包

2021 年,Chrome Aurora 团队推出了脚本组件,以提高 Next.js 中第三方脚本的加载性能。自发布以来,我们扩展了其功能,使开发者能够更轻松、更快速地加载第三方资源。

本文概要介绍了我们发布的新功能(最值得注意的是 @next/third-parties 库),并简要介绍了我们路线图中未来的计划。

第三方脚本对性能的影响

Next.js 网站中41% 的所有第三方请求都是脚本。与其他内容类型不同,脚本可能需要大量时间来下载和执行,这可能会阻止呈现并延迟用户互动。Chrome 用户体验报告 (CrUX) 中的数据显示,加载更多第三方脚本的 Next.js 网站的 Interaction to Next Paint (INP)Largest Contentful Paint (LCP) 通过率较低。

条形图:随着加载的第三方数量的增加,获得良好 INP 和 LCP 得分的 Next.js 所占百分比会下降
2023 年 12 月 CrUX 报告(110,823 个网站)

此图表中观察到的相关性并不意味着存在因果关系。不过,本地实验提供了更多证据,证明第三方脚本会显著影响网页性能。例如,下图比较了在将 Google 跟踪代码管理器容器(包含 18 个随机选择的代码)添加到热门 Next.js 示例应用 Taxonomy 后,各种实验室指标的变化情况。

条形图,显示在启用和不启用 Google 跟踪代码管理器的情况下,网站加载时各种实验室指标的差异
WebPageTest(移动网络 4G - 美国弗吉尼亚州)

WebPageTest 文档详细介绍了如何衡量这些时间。一目了然的是,所有这些实验室指标都受到 GTM 容器的影响。例如,Total Blocking Time (TBT)(一种近似于 INP 的实用实验室代理)增加了近 20 倍。

脚本组件

在 Next.js 中发布 <Script> 组件时,我们确保通过与传统 <script> 元素极其相似的易用 API 引入了该组件。借助该功能,开发者可以在应用中的任何组件中共置第三方脚本,Next.js 会在关键资源加载后负责对脚本进行排序。

<!-- By default, script will load after page becomes interactive -->
<Script src="https://example.com/sample.js" />

<!-- Script is injected server-side and fetched before any page hydration occurs -->
<Script strategy=”beforeInteractive” src="https://example.com/sample.js" />

<!-- Script is fetched later during browser idle time -->
<Script strategy=”lazyOnload” src="https://example.com/sample.js" />

数万个 Next.js 应用(包括 PatreonTargetNotion 等热门网站)都使用 <Script> 组件。尽管该功能很实用,但一些开发者对以下方面提出了疑虑:

  • 在遵循不同第三方提供商的各种安装说明的同时,在 Next.js 应用中放置 <Script> 组件的位置(开发者体验)
  • 对于不同的第三方脚本(用户体验),哪种加载策略最适用。

为了解决这两个问题,我们推出了 @next/third-parties,这是一个专用库,提供一组针对热门第三方平台量身优化的组件和实用程序。

开发者体验:更轻松地管理第三方库

大量 Next.js 网站都使用了许多第三方脚本,其中 Google 跟踪代码管理器最受欢迎,分别有 66% 的网站在使用。@next/third-parties<Script> 组件的基础上构建,通过引入旨在简化这些常见用例的使用方式的更高级别封装容器。

import { GoogleAnalytics } from "@next/third-parties/google";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleTagManager gtmId="GTM-XYZ" />
    </html>
  );
}

Google Analytics 是另一种广泛使用的第三方脚本(Next.js 网站的 52%)也有自己的专用组件。

import { GoogleAnalytics } from "@next/third-parties/google";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleAnalytics gaId="G-XYZ" />
    </html>
  );
}

@next/third-parties 简化了加载常用脚本的流程,但也扩展了我们为其他第三方类别(例如嵌入)开发实用程序的能力。例如,8%4% 的 Next.js 网站分别使用了 Google 地图和 YouTube 嵌入,我们还发布了组件,以便更轻松地加载这些内容。

import { GoogleMapsEmbed } from "@next/third-parties/google";
import { YouTubeEmbed } from "@next/third-parties/google";

export default function Page() {
  return (
    <>
      <GoogleMapsEmbed
        apiKey="XYZ"
        height={200}
        width="100%"
        mode="place"
        q="Brooklyn+Bridge,New+York,NY"
      />
      <YouTubeEmbed videoid="ogfYd705cRs" height={400} params="controls=0" />
    </>
  );
}

用户体验:提高第三方库的加载速度

在理想情况下,每个广泛采用的第三方库都会经过全面优化,因此无需任何用于提升其性能的抽象。不过,在这成为现实之前,我们可以尝试通过 Next.js 等热门框架进行集成,以改善用户体验。我们可以尝试不同的加载技术,确保脚本以正确的方式排序,并最终与第三方提供商分享反馈,以推动上游更改。

以 YouTube 嵌入内容为例。其中一些替代实现的性能远远优于原生嵌入。目前,由 @next/third-parties 导出的 <YouTubeEmbed> 组件使用 lite-youtube-embed,在“Hello, World”Next.js 对比演示中,该组件的加载速度明显更快。

显示 YouTube 嵌入组件与常规 YouTube iframe 之间页面加载时间对比情况的 GIF
WebPageTest(移动 4G - 美国弗吉尼亚州)

同样,对于 Google 地图,我们将 loading="lazy" 作为嵌入的默认属性,以确保地图仅在距离视口一定距离时才加载。这似乎是一个显而易见的要添加的属性,尤其是因为 Google 地图文档在其示例代码段中包含了它,但只有 45% 的嵌入 Google 地图的 Next.js 网站在使用 loading="lazy"

在 Web Worker 中运行第三方脚本

我们正在 @next/third-parties 中探索的一项高级技术是,能够更轻松地将第三方脚本分流到 Web Worker。这种模式在 Partytown 等库中很受欢迎,可以将第三方脚本完全移出主线程,从而显著降低对页面性能的影响。

下面的动画 GIF 显示了在 Next.js 网站中对 GTM 容器应用不同的 <Script> 策略时,长任务和主线程阻塞时间的变化情况。请注意,虽然在策略选项之间切换只会延迟这些脚本的执行时间,但将它们重新定位到 Web Worker 会完全消除它们在主线程上的时间。

GIF,显示不同脚本策略的主线程阻塞时间差异
WebPageTest(移动网络 4G - 美国弗吉尼亚州)

在本例中,将 GTM 容器及其关联的代码脚本的执行移至 Web 工作器后,TTB 缩短了 92%

值得注意的是,如果不小心管理,此方法可能会静默地破坏许多第三方脚本,使调试变得非常困难。在未来几个月内,我们将验证 @next/third-parties 提供的任何第三方组件在 Web Worker 中运行时是否正常运行。如果是这样,我们将努力为开发者提供一种简单的可选方法,以便其使用此方法。

后续步骤

在开发此软件包的过程中,我们发现需要集中第三方加载建议,以便其他框架也能受益于所使用的相同底层技术。这促使我们构建了 Third Party Capital,这是一个使用 JSON 描述第三方加载技术的库,目前是 @next/third-parties 的基础。

接下来,我们将继续专注于改进为 Next.js 提供的组件,并扩大工作范围,在其他热门框架和 CMS 平台中添加类似实用程序。我们目前正与 Nuxt 维护者合作,并计划在不久的将来发布为其生态系统量身定制的类似第三方实用程序。

如果您在 Next.js 应用中使用的某个第三方受 @next/third-parties 支持,请安装该软件包并试用!我们非常期待收到您在 GitHub 上提供的反馈。