2021 年,Chrome Aurora 团队推出了脚本组件,以提高 Next.js 中第三方脚本的加载性能。自该工具发布以来,我们不断扩展其功能,让开发者能够更轻松、更快速地加载第三方资源。
本文概要介绍了我们发布的新功能(最值得注意的是 @next/third-parties 库),并简要介绍了我们路线图中未来的计划。
第三方脚本对性能的影响
在 Next.js 网站中,所有第三方请求中有 41% 是脚本。与其他内容类型不同,脚本可能需要大量时间来下载和执行,这可能会阻止呈现并延迟用户互动。Chrome 用户体验报告 (CrUX) 中的数据表明,加载更多第三方脚本的 Next.js 网站的Interaction to Next Paint (INP) 和 Largest Contentful Paint (LCP) 通过率都较低。
在此图表中观察到的相关性并不意味着存在因果关系。但是,本地实验提供了额外的证据,表明第三方脚本显著影响了网页性能。例如,下图比较了将 Google 跟踪代码管理器容器(由 18 个随机选择的标记)添加到 Taxonomy(一款热门的 Next.js 示例应用)时的各种实验室指标。
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 应用(包括 Patreon、Target 和 Notion 等热门网站)都使用 <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 是另一种广泛使用的第三方脚本(52% 的 Next.js 网站使用了该脚本),它也有一个专用的组件。
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 比较中演示时,加载速度要快得多。
同样,对于 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 会完全消除它们在主线程上的时间。
在本例中,将 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 上提供的反馈。