mcpField
T
third-party

third-party

github.com/patternsdev开发2571.5k 次安装28 周前更新
代码仓库

关于 third-party

third-party 是面向 Web 开发者的第三方资源加载优化指南,涵盖脚本、嵌入内容及其对页面性能的影响。它提供延迟执行、提前建立连接和懒加载等策略,并讨论自托管与缓存。

主要功能

  1. 01用 async 或 defer 延迟脚本
  2. 02使用 preconnect 和 dns-prefetch
  3. 03懒加载首屏以下嵌入内容
  4. 04评估第三方资源性能影响
  5. 05探讨自托管与缓存策略

适用场景

  1. 01降低分析脚本对渲染的阻塞
  2. 02延迟加载视频和地图嵌入
  3. 03评估第三方代码对页面性能的影响

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill third-party

安装后在 Claude Code 中输入/third-party确认已加载。

详细介绍

优化第三方资源加载

目录

适用场景

  • 当第三方脚本(分析、广告、聊天小组件、社交媒体嵌入内容)拖慢网站速度时,使用此方法
  • 这有助于优化 Core Web Vitals,同时保留必要的第三方功能

操作指南

  • 为非关键第三方脚本使用 async 或 defer 属性
  • 使用 preconnect 和 dns-prefetch 资源提示提前建立连接
  • 使用 IntersectionObserver 或外观替代方案,延迟加载首屏以下的嵌入内容(YouTube、地图、社交媒体)
  • 考虑自行托管关键第三方脚本,以更好地控制缓存
  • 使用 Next.js Script 组件,并设置适当的 strategy 值,从框架层面进行优化

详情

很难找到一个与世隔绝运行的现代网站。大多数网站都会与网络上的多个其他来源共存,并依赖它们提供数据、功能、内容等。任何位于其他域名、且由你的网站使用的资源,都是你网站的第三方(3P)资源。网站中常见的第三方资源包括:

  • 地图、视频、社交媒体和聊天服务的嵌入内容
  • 广告
  • 分析组件和标签管理器
  • A/B 测试和个性化脚本
  • 提供现成辅助函数的实用程序库,例如用于数据可视化或动画的库。
  • 用于机器人检测的 reCAPTCHA 或 CAPTCHA。

你可以使用第三方资源集成其他功能,为内容增添价值,或减少从零开始构建网站所需的一些繁琐工作。根据 2021 年 Web Almanac 报告,网络上超过 94% 的页面 使用第三方资源,其中 图像和 JavaScript 是第三方内容的主要组成部分。

第三方资源可以为网站增添有价值的功能,但也可能拖慢网站速度,原因包括:

  • 每个所需资源都会导致浏览器与第三方域名之间产生额外的往返请求。
  • 大量使用 JavaScript(影响下载和执行时间),或因图像/视频未优化而体积庞大。
  • 网站所有者无法影响其具体实现,且其行为可能难以预测。
  • 它们可能阻塞页面上其他关键资源的渲染,并影响 Core Web Vitals(CWV)。

尽管存在这些问题,第三方资源对你的业务仍可能不可或缺。如果无法弃用第三方资源,最好的做法就是对其进行优化,以降低对性能的影响。

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

你可以结合使用多种技术,找出第三方代码对网站的影响。

优化策略

由于第三方代码不在你的控制范围内,因此你无法直接优化这些库。为此,你有两个选择。

  1. 替换或移除:如果第三方脚本带来的价值与其性能开销不成正比,可以考虑移除它。你也可以评估其他轻量级替代方案,看它们是否能提供类似的功能。

  2. 优化加载顺序:加载过程涉及在浏览器中加载多个第一方和第三方资源。要设计最佳加载策略,需要考虑浏览器为不同资源分配的优先级、资源在页面上的位置,以及每项资源对网页的价值。

高效加载 3P 脚本

以下是经过实践验证的最佳做法,正确使用时可以降低第三方资源对性能的影响。

使用 async 或 defer,避免脚本阻塞其他内容

适用范围: 非关键脚本(标签管理器、分析工具)

JavaScript 默认采用同步下载和执行,这可能会阻塞主线程上的 HTML 解析器和 DOM 构建。在 <script> 元素中使用 async 或 defer 属性,可以告知浏览器异步下载脚本。你可以用这些属性下载任何非关键渲染路径所必需的脚本(例如,主要 UI 组件)。

  • defer:解析器运行时并行获取脚本,并将脚本执行延迟到解析完成之后。要将执行延迟到 DOM 构建完成后,默认应选择 defer。
  • async:解析期间并行获取脚本,但脚本一旦可用就会立即执行,此时会阻塞解析器。对于带有依赖项的模块脚本,脚本及其所有依赖项都会在 defer 队列中执行。对于需要在加载过程中较早运行的脚本,应使用 async。例如,你可能希望尽早执行特定的分析脚本,以免错过页面加载早期的数据。
<script src="https://example.com/deferthis.js" defer></script>
<script src="https://example.com/asyncthis.js" async></script>

这里有一点需要注意:async 和 defer 会降低浏览器分配给资源的优先级,导致资源加载明显延后。借助 优先级提示这一新功能,可以缓解这个问题。

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

适用范围: 来自第三方 CDN 的关键脚本、字体、CSS、图片

连接到第三方来源可能会很慢,因为每个第三方服务都可能需要进行 DNS 查询、重定向和多次往返通信。资源提示 dns-prefetch 和 preconnect 可以提前发起连接,缩短这项准备工作所需的时间。

添加与域名对应的 dns-prefetch 资源提示,可以提前进行 DNS 查询,从而减少 DNS 查询所带来的延迟。对于最关键的资源,可以配合使用 preconnect。除了 DNS 查询之外,preconnect 还会通过 TCP 往返通信和 TLS 协商,与第三方域名建立连接。

<head>
  <link rel="preconnect" href="http://example.com" />
  <link rel="dns-prefetch" href="http://example.com" />
</head>

这项案例研究清楚地展示了使用资源提示的好处:Andy Davies 讨论了如何通过与第三方图片 CDN 提前建立连接,使用 preconnect 缩短主产品图片的加载时间。

同样,你也可以使用资源提示来优化关键第三方服务(例如机器人检测服务 reCaptcha 和同意管理服务)的加载时间。

延迟加载首屏以下的第三方资源

适用于: 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 中行为略有不同。首次轻点或点击会加载实际的视频嵌入内容。用户需要再次轻点才能播放视频。
  • 如果未指定嵌入内容的尺寸,延迟加载可能导致布局偏移,影响用户体验。为避免布局偏移,应为所有延迟加载的嵌入内容或其容器元素指定尺寸。
自托管第三方脚本,避免往返请求

适用于: JavaScript 文件、字体

虽然 preconnect 或 dns-prefetch 可以让你提前与第三方源建立连接,但仍然需要这些连接。此外,对于第三方源,你必须依赖其缓存策略,而这种策略可能并不理想。

在同一源上自托管脚本副本,可以让你更好地控制脚本的加载和缓存过程。自托管可缩短 DNS 查询所需的时间,还能让你通过 HTTP caching 优化脚本的缓存策略。你还可以使用 HTTP/2 server push 推送你确定用户需要的脚本。一个很好的第三方脚本自托管示例是 Casper.com,它通过自托管由 Optimizely 提供的第三方脚本,将其首页的开始渲染时间缩短了 1.7 秒。

自托管第三方脚本副本时,你必须确保根据原始脚本的变更定期更新副本。如果不更新,脚本可能会过时,缺少重要的修复内容或依赖项相关变更。在服务器而非 CDN 上自托管,也会让你无法利用 CDN 所采用的 边缘缓存机制。

尽可能使用 service worker 缓存脚本

适用于: JavaScript 文件、字体

对于经常变化的脚本,自行托管可能并非可行的选择。你可以使用 service worker 改善此类第三方脚本的缓存,同时利用 CDN 边缘缓存。这种方法能让你更好地控制通过网络重新获取资源的频率。你还可以将此方法与预连接结合使用,进一步降低获取资源的网络开销。你也可以按需加载资源,让非必要的第三方脚本请求延迟到页面到达关键用户时刻后再发出。

遵循理想的加载顺序

针对不同类型的第三方资源及其对页面的价值,参考上述指南。根据每种资源的预期用途,你可以遵循理想的资源加载顺序,优化地交错加载第一方和第三方资源,从而加快页面加载速度。

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

有些脚本比其他脚本更容易优化。普遍的看法是,大多数用户要等到页面内容显示达到一定程度后才会与网站互动。

非关键 JavaScript

大多数第三方资源(如聊天小组件或分析脚本)并非用户体验的关键组成部分,可以延迟加载。使用 defer 脚本属性是延迟加载和执行这些脚本最常见的方法。

广告或分析团队可能担心延迟脚本会影响应用的曝光度和广告收入。Telegraph 案例研究常被用作例证:延迟所有脚本并未影响任何分析或广告指标。相反,“首次广告加载”指标平均提升了 4 秒。

机器人检测/ReCaptcha

为了防止机器人访问网页表单,开发者通常会尽早加载这些脚本。不过,ReCaptcha 的 JS 负载和主线程占用都相当大,因此可以考虑推迟加载,直到确有需要时再加载。优化此脚本的方法包括:

  1. 仅在少数包含用户输入表单、可能遭到机器人垃圾信息攻击的页面上加载。
  2. 当用户与表单元素互动时(例如聚焦表单时),延迟加载脚本。
  3. 如果需要脚本在页面加载时执行,可以使用资源提示提前建立连接。
Google Tag Manager(GTM)

大型网站通常会向营销团队或代理机构提供 Google Tag Manager 访问权限,以便他们向网站的所有页面添加新的营销标签,改善追踪效果。性能并非营销团队的首要考虑,而且他们可能并未意识到,不加考虑地添加标签会拖慢网站。优化 GTM 脚本,重点在于控制谁可以访问 GTM,以及监控他们所做的更改。

你可以先确保账号归网站所有者所有,而非外部代理机构。这样便可以为添加、编辑和发布标签的人设置细粒度的访问权限。加强开发部门与营销部门之间的协作,还能审核新标签并移除未使用的标签。

你的网站可能不需要在所有页面上使用 GTM。应逐个审核页面,移除不必要的 GTM 引用。使用 Cookie 横幅的网站也可以选择在用户拒绝 Cookie 时不加载 GTM。最后,如果必须在某个页面加载 GTM,可以延迟脚本,使其在主要内容加载完成后再运行。

A/B 测试和个性化

网站会进行 A/B 测试,以确定哪个网页版本的表现更好。A/B 测试会对运行测试的页面造成显著影响,每项测试最多会使加载时间增加 1 秒。目前,许多 A/B 测试都通过第三方提供,开发者对用于更改测试界面(UI)的 JavaScript 代码几乎没有控制权。

网站个性化是一个相关概念,指运行脚本,根据已知数据为不同用户提供定制体验。这些脚本同样臃肿且难以优化。与 A/B 测试脚本一样,个性化脚本也需要尽早运行,因为渲染后的界面(UI)取决于脚本的输出。开发基于服务器的自定义 A/B 测试解决方案和个性化功能,是优化 A/B 测试的理想方法,但未必总是可行。

要优化第三方 A/B 测试脚本,可以限制接收脚本的用户数量。Google Optimize 允许配置用户定位规则。

YouTube 和地图嵌入内容

这些嵌入内容开销较大,开发者必须考虑采用延迟加载或点击后加载模式来加载它们,从而进行优化。建议使用 lite-youtube-embed 等解决方案,但请注意,在 iOS/macOS-Safari 中使用此外观播放视频需要双击或双击触控。

社交媒体嵌入内容

一些社交媒体嵌入内容提供延迟加载脚本的选项(例如 Facebook 嵌入内容中的 data-lazy)。你可以尝试使用此选项来提升性能。另一种方法是手动创建图片外观,或使用 tweetpik 等工具创建。

开箱即用的优化

要优化第三方内容,开发团队应了解资源提示、延迟加载、HTTP 缓存和 Service Worker 的细节,然后在解决方案中加以实现。一些框架和库已将这些最佳实践封装起来,方便开发者使用。

Builder.io 创建的 Partytown 是一个实验性库,可将资源密集型脚本放到网页工作线程中运行,而不是放在主线程中。其理念是,主线程应专用于运行你的代码,任何不属于关键路径所需的脚本都可以放入网页工作线程中进行沙箱隔离。Partytown 允许你配置对主线程 API 的访问权限,例如 cookies、localStorage、userAgent 等。你也可以记录 API 调用及其参数,以便更好地了解脚本的行为。

JavaScript 代理和 Service Worker 负责处理网页工作线程与主线程之间的通信。Partytown 脚本必须与 HTML 文档托管在同一服务器上。它可以与 React 或 Next.js 应用搭配使用,也可以在没有任何框架的情况下使用。任何可以在网页工作线程中运行的第三方脚本,都应将其起始 script 标签的 type 属性设为 text/partytown,如下所示。

<script type="text/partytown">// Third-party analytics scripts</script>

该库还提供了 React Partytown 组件,可直接加入 React 或 Next.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 还为常见的分析库(例如 Google Tag Manager)提供了 React 组件:

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 组件

Next.js 11 于 2021 年年中发布,其组件基于 Google Aurora 团队提出的 Conformance 方法论。Next.js Script 组件通过提供可自定义的模板来提升加载性能,从而运用 Conformance。Script 组件封装了 <script> 标签,并允许你使用 strategy 属性设置第三方脚本的加载优先级。strategy 属性可以采用三个值:

  1. beforeInteractive:用于浏览器应在页面可交互之前执行的关键脚本。(例如,机器人检测)
  2. afterInteractive:用于浏览器可在页面可交互后运行的脚本。(例如,标签管理器)这是默认采用的策略,效果等同于使用 defer 加载脚本
  3. lazyOnload:用于浏览器空闲时可以延迟加载的脚本。

设置策略有助于 Next.js 自动应用优化和最佳实践来加载脚本,同时确保最佳加载顺序。

之前:

import Head from "next/head";

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

之后:

import Script from 'next/script'

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

如果你希望尽早加载适用于核心内容的特定 polyfill,可以使用 beforeInteractive 策略:

import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
        strategy="beforeInteractive"
      />
    </>
  );
}
延迟加载社交媒体嵌入内容

社交媒体嵌入内容,尤其是页面加载时不可见的内容,可以延后加载或延迟加载。你可以使用 lazyOnload 策略:

import Script from "next/script";

export default function Home() {
  return (
    <>
      <Script
        src="https://connect.facebook.net/en_US/sdk.js"
        strategy="lazyOnload"
      />
    </>
  );
}
有条件地在加载时执行代码

某些代码可能需要在特定第三方组件加载后执行。你可以在 Script 组件的 onLoad 属性中指定:

<Script
  src={url} // consent management
  strategy="beforeInteractive"
  onLoad={() => {
    // If loaded successfully, then you can load other scripts in sequence
  }}
/>
在 script 标签中使用内联脚本

需要根据第三方组件的加载情况执行的内联脚本,也可以包含在 Script 组件中:

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')`
 }}
/>

这里使用内联脚本,在第三方横幅广告延迟加载后更改其可见性。请注意,也可以使用 dangerouslySetInnerHTML 属性来包含内联脚本。

向第三方脚本传递属性

你可以在 Script 组件中设置第三方脚本可用的特定属性值:

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"
      />
    </>
  );
}
加载分析脚本

你可以通过 Google Analytics(GA)和 Google Tag Manager(GTM)以不同方式在网站中加入分析功能。你可以使用 Script 组件,在 Next.js 网站上以最佳方式加载 gtag.js 或 analytics.js 脚本。

在 _app.js 中包含 Script 组件,即可为网站上的所有页面启用 GTM:

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;

请注意,在以上两个示例中,分析脚本都是通过 strategy = afterInteractive 加载的。

结论

在组合网页时,如果将来自自己服务器的资源与来自网络其他位置的资源结合使用,就必须经常监控这些资源之间的相互影响。你可以先正确安排资源加载顺序,并遵循最佳实践。也可以依靠将这些最佳实践内置于设计中的框架或解决方案。

随着网站不断发展,性能报告和定期审计有助于消除冗余,并优化影响性能的脚本。最后,我们始终可以期待那些存在广为人知的性能问题的第三方能够在其代码端进行优化,或开放 API,以便通过变通方案解决这些问题。

来源

评论

支持 Markdown