用于管理第三方库的 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 个随机选择的标记)添加到 Taxonomy(热门的 Next.js 示例应用)时的各种实验室指标。

一张条形图,显示使用和不使用 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> 组件。尽管这种方法有效,但一些开发者却引发了对以下问题的担忧:

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

为了解决这两个问题,我们推出了 @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 工作器中运行第三方脚本

我们正在 @next/third-parties 中探索的一项高级技术是,可以更轻松地将第三方脚本分流到 Web 工作器。该方法在 Partytown 等库中普及,可以将第三方脚本完全移出主线程,从而显著降低第三方脚本对网页性能的影响。

以下动画 GIF 展示了在 Next.js 网站中对 GTM 容器应用不同的 <Script> 策略时,长时间运行的任务和主线程阻塞时间的变化。请注意,虽然切换策略选项只会延迟这些脚本的执行时间,但将它们转移到 Web 工作器会完全消除它们在主线程上花费的时间。

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

在这个特定示例中,将 GTM 容器及其关联代码脚本的执行移至 Web 工作器后,TBT 减少了 92%

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

后续步骤

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

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

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