third-party

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Optimize loading third-parties

优化第三方脚本加载

tl;dr: Third-party resources can slow down sites and can be a challenge to optimize. You can follow certain best practices to load or delay different types of third-parties efficiently. You can also use framework-level components such as the Next.js Script component, which provides a template for framing the "when" and "how" for loading third-party scripts. Alternatively, experimental ideas like Partytown may be of interest.
简而言之:第三方资源会拖慢网站速度,优化起来颇具挑战。你可以遵循一些最佳实践,高效地加载或延迟不同类型的第三方脚本。你也可以使用框架级组件,比如Next.js Script组件,它为加载第三方脚本的时机和方式提供了模板。另外,像Partytown这样的实验性方案也值得关注。

When to Use

适用场景

  • Use this when third-party scripts (analytics, ads, chat widgets, social embeds) are slowing down your site
  • This is helpful for optimizing Core Web Vitals while retaining essential third-party functionality
  • 当第三方脚本(分析工具、广告、聊天组件、社交嵌入内容)拖慢网站速度时使用
  • 有助于在保留第三方核心功能的同时优化核心网页指标(Core Web Vitals)

Instructions

操作指南

  • Use
    async
    or
    defer
    attributes for non-critical third-party scripts
  • Establish early connections with
    preconnect
    and
    dns-prefetch
    resource hints
  • Lazy-load below-the-fold embeds (YouTube, maps, social media) using IntersectionObserver or facades
  • Consider self-hosting critical third-party scripts for better caching control
  • Use the Next.js Script component with appropriate
    strategy
    values for framework-level optimization
  • 为非关键第三方脚本添加
    async
    defer
    属性
  • 使用
    preconnect
    dns-prefetch
    资源提示提前建立连接
  • 使用IntersectionObserver或占位符懒加载视口外的嵌入内容(YouTube、地图、社交媒体)
  • 考虑自托管关键第三方脚本以获得更好的缓存控制
  • 使用带有合适
    strategy
    值的Next.js Script组件进行框架级优化

Details

详细说明

It would be hard to find a modern site that operates in silos. Most sites coexist and depend on several other sources on the web for data, functions, content, and much more. Any resource located on another domain and consumed by your site is a third-party (3P) resource for your site. Typical third-party resources that are included on sites include:
  • Embeds for maps, videos, social media, and chat services
  • Advertisements
  • Analytics components and tag managers
  • A/B testing and personalization scripts
  • Utility libraries that provide ready-to-use helper functions such as those used for data visualization or animations.
  • reCAPTCHA or CAPTCHA for bot detection.
You can use third-parties to integrate other features that add value to your content or to reduce some drudgery involved in building a site from scratch. As per the Web Almanac report for 2021, more than 94% of the pages on the web use third-parties - images and JavaScript forming the most significant contributors to third-party content.
While third-party resources can enrich your site with valuable features, they can also slow it down if:
  • They cause additional round trips to the third-party domain for every required resource.
  • They make heavy usage of JavaScript (impacting download and execution time) or are bulky in size because of unoptimized images/videos.
  • Individual site owners cannot influence the implementation, and their behavior can be unpredictable.
  • They can block the rendering of other critical resources on the page and affect Core Web Vitals (CWV).
Despite these issues, third-parties may be essential to your business. If you cannot do away with 3P's, the next best thing is to optimize them to reduce performance impact.
很难找到一个完全独立运行的现代网站。大多数网站都会与网络上的其他数据源共存并依赖它们获取数据、功能、内容等。任何位于其他域名并被你的网站使用的资源,都属于你的网站的第三方(3P)资源。网站上常见的第三方资源包括:
  • 地图、视频、社交媒体和聊天服务的嵌入内容
  • 广告
  • 分析组件和标签管理器
  • A/B测试和个性化脚本
  • 提供现成辅助功能的工具库,比如用于数据可视化或动画的库
  • reCAPTCHA或其他验证码工具,用于检测机器人
你可以借助第三方资源为网站添加增值功能,也可以减少从零搭建网站的繁琐工作。根据2021年Web Almanac报告,超过94%的网页使用第三方资源——其中图片和JavaScript是第三方内容的主要组成部分。
虽然第三方资源能为网站增添有价值的功能,但在以下情况下也会拖慢网站速度:
  • 每次加载所需资源时,都需要与第三方域名进行额外的网络往返
  • 大量使用JavaScript(影响下载和执行时间),或因未优化的图片/视频导致体积过大
  • 网站所有者无法控制其实现方式,行为不可预测
  • 会阻塞页面上其他关键资源的渲染,影响核心网页指标(Core Web Vitals)
尽管存在这些问题,第三方资源可能对业务至关重要。如果你无法舍弃第三方资源,次优选择就是对它们进行优化以降低性能影响。

Assessing the performance impact of 3P resources

评估第三方资源的性能影响

You can use a combination of techniques to find how third-party code is affecting your site.
你可以结合多种技术来发现第三方代码对网站的影响:

Optimization strategies

优化策略

Since third-party code is not under your control, you cannot optimize the libraries directly. This leaves you with two options.
  1. Replace or remove: If the value provided by the third-party script is not proportional to its performance cost, consider removing it. You can also evaluate other alternatives that are lightweight but provide similar functionality.
  2. Optimize the loading sequence: The loading process involves loading several first-party and third-party resources in the browser. To design an optimal loading strategy, you would need to consider the browser assigned priority for different resources, their position on the page, and the value of each resource for the web page.
由于第三方代码不在你的控制范围内,你无法直接优化这些库。这给你留下两个选择:
  1. 替换或移除:如果第三方脚本带来的价值与其性能成本不成正比,可以考虑移除它。你也可以评估其他轻量级但功能相似的替代方案。
  2. 优化加载顺序:加载过程涉及在浏览器中加载多个第一方和第三方资源。要设计最优加载策略,你需要考虑浏览器为不同资源分配的优先级、它们在页面上的位置,以及每个资源对网页的价值。

Load 3P scripts efficiently

高效加载第三方脚本

Following are the time-tested best practices that can reduce the performance impact of third-party resources when used correctly.
以下是经过实践验证的最佳实践,正确使用可以降低第三方资源的性能影响:

Use async or defer to prevent scripts from blocking other content.

使用async或defer防止脚本阻塞其他内容

Applicable to: Non-critical scripts (tag managers, analytics)
JavaScript download and execution is synchronous by default and can block the HTML parser and DOM construction on the main thread. Use of async or defer attributes in the
<script>
element tells the browser to download scripts asynchronously. You can use these to download any script that is not necessary for the critical rendering path (e.g., the main UI component).
  • defer
    : the script is fetched in parallel as the parser executes, and script execution is delayed till the parsing is complete. Defer should be the default choice for delaying execution until after DOM construction.
  • async
    : the script is fetched in parallel while parsing but executed as soon as it is available when it blocks the parser. For module scripts with dependencies, the script and all its dependencies are executed in the defer queue. Use
    async
    for scripts that need to run earlier in the loading process. For example, you may want to execute specific analytics scripts early without missing any early page-load data.
html
<script src="https://example.com/deferthis.js" defer></script>
<script src="https://example.com/asyncthis.js" async></script>
One caveat worth mentioning here is that async and defer lower the browser assigned priority of the resources causing it to load significantly later. A new feature for priority hints can help to work around this problem.
适用对象:非关键脚本(标签管理器、分析工具)
JavaScript默认是同步下载和执行的,会阻塞HTML解析和主线程上的DOM构建。在
<script>
元素中使用asyncdefer属性,告诉浏览器异步下载脚本。你可以对非关键渲染路径所需的脚本(例如主UI组件之外的脚本)使用这些属性。
  • defer
    :脚本在解析器执行时并行获取,执行延迟到解析完成后。Defer应该是延迟执行到DOM构建完成后的默认选择。
  • async
    :脚本在解析时并行获取,但一旦可用就立即执行,此时会阻塞解析器。对于带有依赖的模块脚本,脚本及其所有依赖项会在defer队列中执行。对需要在加载过程早期运行的脚本使用
    async
    ,例如,你可能希望尽早执行特定的分析脚本,以免遗漏早期页面加载数据。
html
<script src="https://example.com/deferthis.js" defer></script>
<script src="https://example.com/asyncthis.js" async></script>
需要注意的一点是,async和defer会降低浏览器为资源分配的优先级,导致资源加载时间明显延后。优先级提示(priority hints)这一新功能可以帮助解决这个问题。

Establish early connections to required origins using resource hints

使用资源提示提前建立与所需源的连接

Applicable to: Critical scripts, fonts, CSS, images from third-party CDNs
Connecting to third-party origins can be slow due to the DNS lookups, redirects, and multiple round trips that may be required for each third-party server. Resource hints dns-prefetch and preconnect help cut down the time needed for this setup by initiating the connections early in the life cycle.
Including a dns-prefetch resource hint corresponding to a domain will perform the DNS lookup early, thus reducing the latency associated with dns lookups. You can pair this with preconnect for the most critical resources. The preconnect initiates a connection with the third-party domain by performing TCP round trips and handling TLS negotiations in addition to the DNS lookup.
html
<head>
  <link rel="preconnect" href="http://example.com" />
  <link rel="dns-prefetch" href="http://example.com" />
</head>
Benefits of using resource hints are evident in this case study where Andy Davies discusses how using preconnect helped reduce the loading time for the main product image by initiating an early connection to the third-party image CDN.
"The real-world metrics showed a 400ms improvement at the median and greater than 1s improvement at the 95th percentile."
Similarly, you may use resource hints to optimize loading time for critical third-parties like bot detection (reCaptcha) and consent management.
适用对象:关键脚本、字体、CSS、来自第三方CDN的图片
连接第三方源可能很慢,因为每个第三方服务器可能需要进行DNS查找、重定向和多次网络往返。资源提示dns-prefetch和preconnect通过在生命周期早期启动连接,帮助减少建立连接所需的时间。
为域名添加dns-prefetch资源提示,会提前执行DNS查找,从而减少DNS查找相关的延迟。对于最关键的资源,你可以将其与preconnect配合使用。除了DNS查找外,preconnect还会通过执行TCP往返和处理TLS协商来启动与第三方域名的连接。
html
<head>
  <link rel="preconnect" href="http://example.com" />
  <link rel="dns-prefetch" href="http://example.com" />
</head>
在Andy Davies的案例研究中,资源提示的优势显而易见,他讨论了如何使用preconnect通过提前连接第三方图片CDN,缩短了主产品图片的加载时间
"真实世界的数据显示,中位数加载时间缩短了400ms,95%分位数的加载时间缩短了超过1s。"
同样,你可以使用资源提示优化关键第三方资源的加载时间,比如机器人检测(reCaptcha)和同意管理工具。

Lazy load below-the-fold 3P resources

懒加载视口外的第三方资源

Applicable to: Embeds such as YouTube, Maps, Advertisements and Social media
Third-party embeds like those used for social media feeds, advertisements, YouTube videos, and maps can slow down web pages. However, all such embeds may not be visible to users on page load and may be lazy-loaded as the user scrolls down to them. You can use different lazy-loading methods depending on desired browser support.
  1. The loading attribute can be used with images and iframes commonly used to load third-party embeds like those for YouTube or Google Maps.
  2. A custom implementation using the IntersectionObserver API allows you to detect when the observed element enters or exits the browser's viewport.
  3. Lazy-sizes - a popular JavaScript library that implements lazy-loading for you.
A variation of lazy-loading embeds uses a static or dynamic facade displayed to users on page loads. Instead of the map embed, you can use a static image of the actual embed. Solutions include the Map Static API for maps, Tweetpik for Twitter embeds, lite-youtube-embed for YouTube, React-live-chat-loader for chat widgets. A comprehensive discussion on these techniques is available here.
A few caveats concerning lazy-loading and facades:
  • The behavior for the YouTube facade is slightly different on iOS and Safari on macOS 11+. Tapping/clicking the first time loads the actual video embed. Users will have to tap again to play the video.
  • Lazy loading can lead to layout shifts and affect the user experience if the size of the embed is not specified. To prevent layout shifts, you should specify the size for all lazy-loaded embeds or their container elements.
适用对象:嵌入内容,如YouTube、地图、广告和社交媒体
第三方嵌入内容(如社交媒体动态、广告、YouTube视频和地图)会拖慢网页速度。但并非所有这些嵌入内容在页面加载时都对用户可见,你可以在用户滚动到它们时再进行懒加载。根据所需的浏览器支持情况,你可以使用不同的懒加载方法:
  1. loading属性可用于图片和iframe,这些元素通常用于加载第三方嵌入内容,如YouTube或Google Maps。
  2. 使用IntersectionObserver API进行自定义实现,检测被观察元素何时进入或离开浏览器视口。
  3. Lazy-sizes——一个流行的JavaScript库,为你实现懒加载功能。
懒加载嵌入内容的一种变体是在页面加载时向用户显示静态或动态占位符。你可以使用实际嵌入内容的静态图片替代地图嵌入,相关解决方案包括用于地图的Map Static API、用于Twitter嵌入的Tweetpik、用于YouTube的lite-youtube-embed、用于聊天组件的React-live-chat-loader。这些技术的全面讨论可参考这里
关于懒加载和占位符的一些注意事项:
  • YouTube占位符在iOS和macOS 11+的Safari上的行为略有不同。第一次点击/触摸会加载实际的视频嵌入内容,用户需要再次点击才能播放视频。
  • 如果未指定嵌入内容的大小,懒加载可能会导致布局偏移,影响用户体验。为防止布局偏移,你应该为所有懒加载的嵌入内容或其容器元素指定大小。

Self-host 3P scripts to prevent round trips

自托管第三方脚本以避免网络往返

Applicable to: JavaScript files, fonts
Although preconnect or dns-prefetch allows you to initiate connections to third-party origins early, the connections are still required. Also, with third-party origins, you need to rely on their caching strategy, which may not be optimal.
Self-hosting a copy of the scripts on the same origin offers you more control over the loading and caching process used for the scripts. Self-hosting reduces the time required for DNS lookup and allows you to improve the caching strategy for the scripts using HTTP caching. You can also use HTTP/2 server push to push scripts that you know the user will need. A great example of how self-hosting third-party scripts is Casper.com which improved the start render time for its home page by 1.7 seconds by self-hosting third-party scripts provided by Optimizely.
With self-hosted copies of third-party scripts, you have to ensure that you update your copy regularly based on changes to the original. Without updates, the script may become outdated, missing important fixes or changes corresponding to dependencies. Self-hosting on a server instead of a CDN will also prevent you from taking advantage of edge-caching mechanisms employed by CDNs.
适用对象:JavaScript文件、字体
虽然preconnect或dns-prefetch允许你提前启动与第三方源的连接,但仍需要建立连接。此外,对于第三方源,你需要依赖它们的缓存策略,而这些策略可能并不理想。
在同一源上自托管脚本副本,能让你更好地控制脚本的加载和缓存过程。自托管减少了DNS查找所需的时间,并允许你使用HTTP缓存改进脚本的缓存策略。你还可以使用HTTP/2服务器推送推送你知道用户需要的脚本。Casper.com就是一个自托管第三方脚本的绝佳例子,它通过自托管Optimizely提供的第三方脚本,将首页的首次渲染时间缩短了1.7秒。
对于自托管的第三方脚本副本,你必须确保根据原始脚本的变化定期更新自己的副本。如果不更新,脚本可能会过时,错过重要的修复或依赖项变更。在服务器而非CDN上自托管,也会让你无法利用CDN采用的边缘缓存机制。

Use service workers to cache scripts where possible

尽可能使用Service Worker缓存脚本

Applicable to: JavaScript files, fonts
Self-hosting may not be an option for scripts that change frequently. You can use service workers to improve caching for such third-party scripts while also utilizing CDN edge caching. This technique gives you better control over the frequency of re-fetches over the network. This technique can be combined with preconnects to further reduce the network cost of the fetch operation. You can also load resources such that requests for non-essential third-party scripts are deferred till the page reaches a key user moment.
适用对象:JavaScript文件、字体
对于频繁变化的脚本,自托管可能不是一个选项。你可以使用Service Worker改善此类第三方脚本的缓存,同时利用CDN边缘缓存。这种技术能让你更好地控制网络重新获取的频率。该技术还可以与preconnect结合使用,进一步降低获取操作的网络成本。你还可以调整资源加载方式,将非必要第三方脚本的请求延迟到页面达到关键用户交互时刻。

Follow the ideal loading sequence

遵循理想的加载顺序

Consider the above guidance for different types of third-parties and their value to the page. Based on the intended use for each resource, you can follow the ideal resource loading sequence to interlace first-party and third-party resources optimally for faster page loads.
结合上述针对不同类型第三方资源及其对页面价值的指导,根据每个资源的预期用途,你可以遵循理想的资源加载顺序,优化交错加载第一方和第三方资源的方式,以实现更快的页面加载。

Best practices by script type

按脚本类型划分的最佳实践

Some scripts are easier to optimize than others. The general consensus was that most users do not interact with a site until a certain threshold of content is visible.
有些脚本比其他脚本更容易优化。普遍的共识是,大多数用户在看到一定量的可见内容之前不会与网站交互。

Non-critical JavaScript

非关键JavaScript

Most third-parties like chat widgets or analytics scripts are not critical to the user experience and can be delayed. Using the
defer
script attribute is the most common method to delay the loading and execution of these scripts.
Advertising or analytics teams may worry about the impact of deferring scripts on the visibility and advertising revenue of the app. The Telegraph case study is often cited in this context, where deferring all scripts did not skew any analytics or advertising metrics. Instead, the First Ad Loaded metric improved by an average of 4 seconds.
大多数第三方资源(如聊天组件或分析脚本)对用户体验来说并非关键,可以延迟加载。使用
defer
脚本属性是延迟这些脚本加载和执行的最常用方法。
广告或分析团队可能会担心延迟脚本会影响应用的可见性和广告收入。Telegraph的案例研究经常被引用,在该案例中,延迟所有脚本并未影响任何分析或广告指标,反而首次广告加载指标平均缩短了4秒。

Bot detection/ReCaptcha

机器人检测/ReCaptcha

Since you would want to prevent bots from accessing web forms, developers would usually load these scripts as early as possible. However, ReCaptcha has a sizable JS payload and main thread footprint, so there is motivation to defer loading it until required. Few methods to optimize this script are:
  1. Load it only on a few pages with form inputs from the user that may get spammed by a bot.
  2. Lazy load the script when the user interacts with form elements, for example, on form focus.
  3. Use resource hints to establish early connections when you need the script to execute on page load.
由于你希望防止机器人访问网页表单,开发人员通常会尽早加载这些脚本。然而,ReCaptcha的JS负载和主线程占用量很大,因此有动机将其延迟到需要时再加载。优化此脚本的几种方法:
  1. 仅在包含用户输入表单(可能被机器人垃圾信息攻击)的少数页面上加载它
  2. 在用户与表单元素交互时懒加载脚本,例如在表单获得焦点时
  3. 当你需要脚本在页面加载时执行时,使用资源提示提前建立连接

Google Tag Manager (GTM)

Google Tag Manager (GTM)

Large sites often provide Google Tag Manager access to marketing teams or agencies. This allows them to add new marketing tags to all pages on the site for better tracking. Performance is not a primary concern for the marketing team, and all of them may not know that inconsiderately adding tags can slow down the site. Optimization of GTM scripts is more about controlling who accesses GTM and monitoring the changes that they make.
You can start by ensuring that the site owners own the account rather than an external agency. This allows you to define granular access permissions for who can add, edit and publish tags. Better collaboration between development and marketing departments can be set up to audit new tags and remove unused tags.
Your site may not need GTM on all pages. Pages should be audited individually so that unnecessary GTM inclusions can be removed. Sites that use cookie banners can also opt not to load GTM if the user declines cookies. Finally, if you must load GTM on a page, you can defer the scripts to fire after loading the main content.
大型网站通常会向营销团队或代理机构提供Google Tag Manager访问权限,允许他们在网站的所有页面上添加新的营销标签以进行更好的跟踪。营销团队主要关注的不是性能,他们中的一些人可能不知道随意添加标签会拖慢网站速度。优化GTM脚本更多是关于控制谁能访问GTM并监控他们所做的更改。
你可以从确保网站所有者拥有账户开始,而不是由外部代理机构拥有。这让你可以定义精细的访问权限,控制谁可以添加、编辑和发布标签。可以建立开发和营销部门之间更好的协作,审核新标签并移除未使用的标签。
你的网站可能不需要在所有页面上都使用GTM。应该单独审核每个页面,移除不必要的GTM引用。使用Cookie横幅的网站还可以选择在用户拒绝Cookie时不加载GTM。最后,如果必须在页面上加载GTM,可以延迟脚本到主内容加载完成后再执行。

A/B Testing and Personalization

A/B测试和个性化

Sites conduct A/B tests to check which version of the webpage performs better. A/B tests can significantly affect the performance of pages on which they are run, with each test adding as much as 1 sec to the loading time. Currently, many A/B tests are sourced externally through third-parties, and developers have little control over the JavaScript code executed to alter the UI for these tests.
Site personalization is a related concept which involves running scripts to provide a tailored experience for different users based on known data. These scripts are again heavy and difficult to optimize. Like A/B testing scripts, personalization scripts also need to run early as the rendered UI depends on the script's output. Developing a custom server-based solution for A/B testing and personalization is the ideal method to optimize A/B testing. However, it may not always be feasible.
To optimize third-party A/B testing scripts, you can limit the number of users who receive the script. Google's Optimize allows the configuration of rules for targeting users.
网站进行A/B测试以检查哪个版本的网页表现更好。A/B测试会显著影响运行测试的页面性能,每次测试最多可能增加1秒的加载时间。目前,许多A/B测试是通过第三方外部提供的,开发人员几乎无法控制为这些测试修改UI而执行的JavaScript代码。
网站个性化是一个相关概念,涉及运行脚本根据已知数据为不同用户提供定制化体验。这些脚本同样体积庞大且难以优化。与A/B测试脚本一样,个性化脚本也需要尽早运行,因为渲染的UI取决于脚本的输出。开发基于服务器的自定义A/B测试和个性化解决方案是优化A/B测试的理想方法,但并非总是可行。
要优化第三方A/B测试脚本,你可以限制接收脚本的用户数量。Google的Optimize允许配置用户定位规则

YouTube and map embeds

YouTube和地图嵌入内容

These embeds are heavy, and developers must explore lazy-loading or click-to-load patterns to load the embeds to optimize them. Use of solutions like lite-youtube-embed is encouraged while noting that double-tap/click is required in iOS/macOS-Safari to play the video using this facade.
这些嵌入内容体积庞大,开发人员必须探索懒加载或点击加载模式来优化它们的加载。建议使用lite-youtube-embed等解决方案,同时注意在iOS/macOS-Safari上使用此占位符时需要双击/点击才能播放视频。

Social media embeds

社交媒体嵌入内容

Some social media embeds provide an option to lazy-load their scripts (e.g., data-lazy in Facebook embeds). You can explore this to improve performance. Another alternative is to use image facades created manually or using tools like tweetpik.
一些社交媒体嵌入内容提供了懒加载脚本的选项(例如Facebook嵌入中的data-lazy)。你可以尝试使用此选项来提升性能。另一种替代方案是手动或使用tweetpik等工具创建图片占位符。

Out-of-the-box optimization

开箱即用的优化方案

To optimize third-parties, development teams should understand the nuances of resource hints, lazy loading, HTTP caching, and service workers and then implement these in their solutions. Some frameworks and libraries have encapsulated these best practices in a way that developers can easily use.
Partytown created by Builder.io is an experimental library that helps run resource-intensive scripts on a web worker instead of the main thread. Their philosophy is that the main thread should be dedicated to your code, and any scripts that are not required by the critical path can be sandboxed and isolated to a web worker. Partytown allows you to configure access to the main thread APIs such as cookies, localStorage, userAgent, etc. API calls may also be logged with arguments to get a better insight into what the scripts do.
JavaScript proxies and a service worker handle communication between the web worker and the main thread. Partytown scripts must be self-hosted on the same server as the HTML documents. It may be used with React or Next.js apps or even without any framework. Each third-party script that can execute in a web server should set the type attribute of its opening script tag to text/partytown as follows.
html
<script type="text/partytown">// Third-party analytics scripts</script>
The library also provides a React Partytown component that you can directly include in your React or Next.js projects:
js
import { Partytown } from '@builder.io/partytown/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
 render() {
   return (
     <Html>
       <Head>
         <Partytown />
       </Head>
       <body>
         <Main />
         <NextScript />
       </body>
     </Html>
   );
 }
Partytown also includes React components for common analytics libraries such as Google Tag Manager:
js
import { Partytown, GoogleTagManager, GoogleTagManagerNoScript } from '@builder.io/partytown/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
 render() {
   return (
     <Html>
       <Head>
         <GoogleTagManager containerId={'GTM-XXXXX'} />
         <Partytown />
       </Head>
       <body>
         <GoogleTagManagerNoScript containerId={'GTM-XXXXX'} />
         <Main />
         <NextScript />
       </body>
     </Html>
   );
 }
要优化第三方资源,开发团队需要了解资源提示、懒加载、HTTP缓存和Service Worker的细节,然后在解决方案中实现这些技术。一些框架和库已经将这些最佳实践封装起来,方便开发人员使用。
由Builder.io创建的Partytown是一个实验性库,帮助将资源密集型脚本在Web Worker而非主线程上运行。他们的理念是主线程应该专用于你的代码,任何非关键路径所需的脚本都可以被沙箱化并隔离到Web Worker中。Partytown允许你配置对主线程API的访问,例如cookies、localStorage、userAgent等。还可以记录API调用及其参数,以便更好地了解脚本的行为。
JavaScript代理和Service Worker负责处理Web Worker和主线程之间的通信。Partytown脚本必须与HTML文档在同一服务器上自托管。它可以用于React或Next.js应用,甚至不使用任何框架。每个可以在Web Worker中执行的第三方脚本,都应将其起始script标签的type属性设置为text/partytown,如下所示:
html
<script type="text/partytown">// Third-party analytics scripts</script>
该库还提供了React Partytown组件,你可以直接将其包含在React或Next.js项目中:
js
import { Partytown } from '@builder.io/partytown/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
 render() {
   return (
     <Html>
       <Head>
         <Partytown />
       </Head>
       <body>
         <Main />
         <NextScript />
       </body>
     </Html>
   );
 }
Partytown还包含针对常见分析库的React组件,例如Google Tag Manager:
js
import { Partytown, GoogleTagManager, GoogleTagManagerNoScript } from '@builder.io/partytown/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
 render() {
   return (
     <Html>
       <Head>
         <GoogleTagManager containerId={'GTM-XXXXX'} />
         <Partytown />
       </Head>
       <body>
         <GoogleTagManagerNoScript containerId={'GTM-XXXXX'} />
         <Main />
         <NextScript />
       </body>
     </Html>
   );
 }

Next.js
Script
component

Next.js
Script
组件

Next.js 11 was released in mid-2021 with components based on the Conformance methodology introduced by Google's Aurora team. The Next.js Script component uses conformance by providing a customizable template that improves loading performance. The Script component encapsulates the
<script>
tag and allows you to set the loading priority for third-party scripts using the strategy attribute. The strategy attribute can take three values:
  1. beforeInteractive: Use this for critical scripts that the browser should execute before the page becomes interactive. (e.g., bot detection)
  2. afterInteractive: Use this for scripts that the browser can run after the page is interactive. (e.g., tag managers) This is the default strategy applied and is equivalent to loading the script with
    defer
  3. lazyOnload: Use this for scripts that can be lazily loaded when the browser is idle.
Setting the strategy helps Next.js automatically apply the optimizations and best practices to load the script while ensuring the best loading sequence.
Before:
js
import Head from "next/head";

export default function Home() {
  return (
    <>
      <Head>
        <script async src="https://example.com/samplescript.js" />
      </Head>
    </>
  );
}
After:
js
import Script from 'next/script'

export default function Home() {
 return (
   <>
     <Script src="https://example.com/samplescript.js" />
   </>
 )
}
Next.js 11于2021年年中发布,其组件基于Google Aurora团队提出的Conformance方法论。Next.js Script组件通过提供可自定义的模板来提升加载性能,从而实现Conformance。该组件封装了
<script>
标签,允许你使用strategy属性设置第三方脚本的加载优先级。strategy属性可以取三个值:
  1. beforeInteractive:用于浏览器应在页面交互前执行的关键脚本(例如机器人检测脚本)
  2. afterInteractive:用于浏览器可在页面交互后运行的脚本(例如标签管理器)。这是默认策略,等效于使用
    defer
    加载脚本
  3. lazyOnload:用于可在浏览器空闲时懒加载的脚本
设置strategy有助于Next.js自动应用优化和最佳实践来加载脚本,同时确保最佳加载顺序。
优化前:
js
import Head from "next/head";

export default function Home() {
  return (
    <>
      <Head>
        <script async src="https://example.com/samplescript.js" />
      </Head>
    </>
  );
}
优化后:
js
import Script from 'next/script'

export default function Home() {
 return (
   <>
     <Script src="https://example.com/samplescript.js" />
   </>
 )
}

Load polyfills early

提前加载Polyfill

In situations where you want specific polyfills that apply to core content to be loaded early, you can use the beforeInteractive strategy:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
        strategy="beforeInteractive"
      />
    </>
  );
}
如果你希望特定适用于核心内容的Polyfill提前加载,可以使用beforeInteractive策略:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
        strategy="beforeInteractive"
      />
    </>
  );
}

Lazy-load social media embeds

懒加载社交媒体嵌入内容

Social media embeds, especially those not visible on page load, can be delayed or lazy-loaded. You can use the
lazyOnload
strategy:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://connect.facebook.net/en_US/sdk.js"
        strategy="lazyOnload"
      />
    </>
  );
}
社交媒体嵌入内容,尤其是页面加载时不可见的内容,可以延迟或懒加载。你可以使用
lazyOnload
策略:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://connect.facebook.net/en_US/sdk.js"
        strategy="lazyOnload"
      />
    </>
  );
}

Execute code conditionally on load

在加载完成后有条件地执行代码

There may be some code that needs to execute after a specific third-party has been loaded. This can be specified in the onLoad attribute of the script component:
js
<Script
  src={url} // consent management
  strategy="beforeInteractive"
  onLoad={() => {
    // If loaded successfully, then you can load other scripts in sequence
  }}
/>
有些代码需要在特定第三方资源加载完成后执行,这可以在Script组件的onLoad属性中指定:
js
<Script
  src={url} // consent management
  strategy="beforeInteractive"
  onLoad={() => {
    // 如果加载成功,则可以按顺序加载其他脚本
  }}
/>

Using inline scripts within the script tag

在脚本标签中使用内联脚本

Inline scripts that need to execute based on load of a third-party component may also be included in the Script component:
js
import Script from 'next/script'

<Script id="show-banner" strategy="lazyOnload">
 {`document.getElementById('banner').removeClass('hidden')`}
</Script>

// or

<Script
 id="show-banner"
 dangerouslySetInnerHTML={{
   __html: `document.getElementById('banner').removeClass('hidden')`
 }}
/>
Here the inline script is used to change the visibility of a third-party banner ad after it is lazy-loaded. Note that inline scripts may also be included using the dangerouslySetInnerHTML attribute.
需要根据第三方组件的加载情况执行的内联脚本也可以包含在Script组件中:
js
import Script from 'next/script'

<Script id="show-banner" strategy="lazyOnload">
 {`document.getElementById('banner').removeClass('hidden')`}
</Script>

// 或者

<Script
 id="show-banner"
 dangerouslySetInnerHTML={{
   __html: `document.getElementById('banner').removeClass('hidden')`
 }}
/>
这里的内联脚本用于在第三方横幅广告懒加载完成后更改其可见性。注意,内联脚本也可以使用dangerouslySetInnerHTML属性添加。

Forward attributes to third-party scripts

向第三方脚本传递属性

You can set specific attribute values that can be used by the third-party script in the Script component:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://www.google-analytics.com/analytics.js"
        id="analytics"
        nonce="XUENAJFW"
        data-test="analytics"
      />
    </>
  );
}
你可以在Script组件中设置特定属性值,供第三方脚本使用:
js
import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://www.google-analytics.com/analytics.js"
        id="analytics"
        nonce="XUENAJFW"
        data-test="analytics"
      />
    </>
  );
}

Load analytics scripts

加载分析脚本

There are different ways to include analytics on your site, using Google Analytics (GA) and Google Tag Manager (GTM). You can use the Script component to load gtag.js or analytics.js scripts optimally on your Next.js site.
GTM may be enabled for all the pages on the site by including the script component inside _app.js:
js
import Script from "next/script";

function MyApp({ Component, pageProps }) {
  return (
    <>
      {/* Google Tag Manager - Global base code */}
      <Script
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
           (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
           new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
           j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
           'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
           })(window,document,'script','dataLayer', '${GTM_ID}');
         `,
        }}
      />
      <Component {...pageProps} />
    </>
  );
}
export default MyApp;
Note that in both examples above, the analytics scripts are loaded with strategy = afterInteractive.
有多种方式在网站上添加分析功能,例如使用Google Analytics (GA)和Google Tag Manager (GTM)。你可以使用Script组件在Next.js网站上优化加载gtag.js或analytics.js脚本。
可以通过在_app.js中包含Script组件,在网站的所有页面上启用GTM:
js
import Script from "next/script";

function MyApp({ Component, pageProps }) {
  return (
    <>
      {/* Google Tag Manager - 全局基础代码 */}
      <Script
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{
          __html: `
           (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
           new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
           j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
           'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
           })(window,document,'script','dataLayer', '${GTM_ID}');
         `,
        }}
      />
      <Component {...pageProps} />
    </>
  );
}
export default MyApp;
注意,在上述两个示例中,分析脚本都是以strategy = afterInteractive加载的。

Conclusion

总结

When composing your web pages combining resources from your servers with those from other corners of the web, you must monitor the interplay between these resources frequently. You could start by sequencing the resources correctly and following best practices. You can also rely on frameworks or solutions that have built-in these best practices into their design.
As the site grows, performance reporting and regular audits can help eliminate redundancies and optimize scripts that affect performance. Lastly, we can always hope that third-parties with commonly known performance issues will optimize code at their end or expose APIs that enable workarounds to address these issues.
当你将自己服务器上的资源与网络上其他来源的资源组合构建网页时,必须经常监控这些资源之间的相互作用。你可以从正确排序资源和遵循最佳实践开始。你也可以依赖那些在设计中内置了这些最佳实践的框架或解决方案。
随着网站的发展,性能报告和定期审计可以帮助消除冗余并优化影响性能的脚本。最后,我们始终希望那些存在已知性能问题的第三方资源能在他们端优化代码,或提供API以实现解决这些问题的变通方案。

Source

来源