Gatsby Script API
对 Gatsby Script API 的支持已在
gatsby@4.15.0中添加。
Gatsby 包含一个内置的 <Script> 组件,有助于高效加载脚本。
它提供了一种声明不同 加载策略 的便捷方法,并且默认加载策略开箱即用地为 Gatsby 用户提供了强大的性能。它支持 带源脚本 和 内联脚本。
无论您是想让 Gatsby 处理脚本管理的繁重工作,还是希望获得最大的灵活性和控制权,Gatsby 的 <Script> 组件都是一项出色的工具。
在您的站点中使用 Gatsby Script
以下是如何在您站点的 JSX 或 TSX 源文件中导入和使用 <Script> 组件的示例
如果您有现有脚本,使用 Gatsby <Script> 组件就像导入 Script 并将小写 script 标签名称更改为大写的 Script 标签名称一样简单,在大多数情况下。
默认情况下,<Script> 组件将在水合后加载您的脚本。有关声明加载策略的更多信息,请参阅 策略 部分。
带源的脚本和内联脚本
您可以指示 <Script> 组件加载两种类型的脚本
- 带源的脚本
- 内联脚本
带源的脚本
带源的脚本提供一个 src 属性,如下所示
<Script> 组件将使用 src 的值来去重加载,因此如果您在同一页面上包含两个具有相同 src 的脚本,则只会加载一个。
如果出于某种原因,您需要在同一页面上加载两个具有相同源的脚本,您可以为每个脚本提供一个可选的、唯一的 id 属性,<Script> 组件将尝试加载两者。
内联脚本
内联脚本必须包含唯一的 id 属性,并且可以通过两种方式定义
- 通过 React 的特殊
dangerouslySetInnerHTML属性 - 通过模板字面量
以下是两者的示例
从功能上看,这两种定义内联脚本的方式是等效的。
策略
您可以通过传递 strategy 属性来声明加载策略。这些是可用的加载策略
post-hydrate(默认) - 在页面水合后加载idle- 在页面空闲后加载off-main-thread(实验性) - 通过 Partytown 在 Web Worker 中加载(主线程外)
这是您可以在 <Script> 组件中声明这些策略的方式
此外,Gatsby 会导出 ScriptStrategy 枚举,如果您愿意,可以在 TSX 文件中使用它。
水合后策略 (默认)
post-hydrate 策略是 默认 的加载策略,如果您未指定 strategy 属性,则会使用该策略。
此策略的优点是,您可以声明脚本应该在 水合 之后 开始加载。这一点很重要,因为水合是使您的页面具有交互性的过程,并且通过使用常规的 <script> 标签(即使应用了 async 或 defer),您也有风险导致您的脚本与水合您页面的框架 JavaScript 并行加载。
这可能对关键的 Web Vital 指标(如 总阻塞时间)产生负面影响。通过使用带有 post-hydrate 策略的 <Script> 组件,您可以确保您的脚本不会干扰您的页面达到交互状态,从而为您的用户提供更好的体验。
post-hydrate 策略非常适合那些希望确保脚本尽早加载而不影响您站点交互时间的场景。
空闲策略
idle 策略类似于 post-hydrate,因为它在水合后加载,但区别在于 idle 会指示浏览器在主线程空闲时加载脚本。
这意味着,如果您的页面正在执行其他关键工作,例如 DOM 操作或其他占用主线程的计算,您的脚本将等到该工作完成后才开始加载。
idle 策略非常适合那些希望确保脚本加载方式不与其他在主线程上进行的工作竞争的场景。
主线程外策略 (实验性)
与 post-hydrate 和 idle 不同,off-main-thread 策略通过 Partytown 在 Web Worker 中加载您的脚本。
这意味着评估脚本的负担不再是主线程需要考虑的问题,从而释放它来处理其他关键任务。
注意 - 由于 Partytown 仍处于 Beta 阶段,因此
off-main-thread策略被认为是 实验性 的。它受某些 限制 的影响,并且根据您的用例,可能需要比其他加载策略更多的配置。
以下是一个配置 <Script> 组件并使用 off-main-thread 策略加载 Google Analytics 的示例
转发集合
Gatsby 将收集页面上的所有 off-main-thread 脚本,并自动将每个页面定义的 Partytown 转发事件 通过 forward 属性合并为一个配置。
forward 属性是 <Script> 组件处理的唯一与 Partytown 相关的属性。
代理配置
提供给 Gatsby <Script> 组件并使用 off-main-thread 策略的所有 URL 都通过 Gatsby 代理到 /__third-party-proxy?url=${YOUR_URL}。
原因是许多第三方脚本 需要代理才能在 Partytown 中运行,因此 Gatsby 包含内置的代理功能以简化此过程。
为了保持代理的安全,您必须在 Gatsby 配置中使用 partytownProxiedURLs 键定义您要代理的绝对 URL。如果这样做,请求将返回 404。
以下是针对上面 Google Analytics 示例的配置方法
这在通过 gatsby develop、gatsby serve 和 Gatsby Cloud 运行您的站点时开箱即用。
在其他提供商上托管需要支持 Gatsby 的 createRedirect 操作,将请求从 /__third-party-proxy?url=${YOUR_URL} 重写为 YOUR_URL,状态码为 200。您可能需要联系您的托管提供商以查看是否支持此功能。
解析 URL
您可以利用 Partytown 的 vanilla config 来处理 off-main-thread 脚本中特定于 Partytown 的行为。其中一个选项是 resolveUrl,它允许您修改 Partytown 处理的 URL。
使用 resolveUrl 的一个用例是使用标签管理器脚本,例如 Google Tag Manager。这些脚本与 Partytown 一起使用具有挑战性,因为它们包含 其他 脚本,这些脚本会发出 其他 请求,这些请求可能需要代理,也可能不需要,具体取决于 CORS 设置。在这种情况下,您可以使用 resolveUrl 来处理这些子脚本 URL。
以下是使用 Google Tag Manager 加载 Google Analytics(本例中为 Universal Analytics)的示例
注意 - 这假定您已在 Google Tag Manager Web 应用程序中 设置 Google Tag Manager 以使用 Universal Analytics。
首先,您加载 Google Tag Manager (GTM) 脚本并发送初始化事件
然后,您在 Partytown 的 vanilla config 中定义 resolveUrl 来处理 Google Tag Manager 加载的 Google Analytics 脚本
最后,您需要将 Google Analytics URL 添加到 partytownProxiedURLs 中,以便 Gatsby 知道该 URL 是安全的,可以进行代理
此时,您的 Google Tag Manager 和 Google Analytics 脚本都应该可以在您的站点上成功加载。
调试
您还可以利用 Partytown 的 vanilla config 来为您的主线程外脚本启用调试模式。
您可能需要将您的开发工具调整到详细日志级别,以便在控制台中看到额外的日志。
局限性
通过利用 Partytown,使用 off-main-thread 策略的脚本也必须注意 Partytown 文档中提到的局限性。虽然该策略可能很强大,但它可能不是所有场景的最佳解决方案。
这些限制需要 Partytown 进行上游更改才能启用
onLoad和onError回调不受支持。请参阅 Partytown 仓库中的 讨论 #199。- 脚本仅在服务器端渲染 (SSR) 导航(例如,常规
<a>标签导航)时加载,而在客户端渲染 (CSR) 导航(例如,Gatsby<Link>导航)时不加载。请参阅 Partytown 仓库中的 问题 #74。
此外,不能在 wrapRootElement API 中使用 off-main-thread 策略,因为脚本收集依赖于位置提供程序。请改用 wrapPageElement API。
在 Gatsby SSR 和 Browser API 中使用
Gatsby 的 <Script> 组件也可以在以下 Gatsby SSR 和 Gatsby Browser API 中使用
wrapPageElementwrapRootElement
注意 - 如果您使用其中一个 API,建议同时在 Gatsby SSR 和 Gatsby Browser 中实现它。一种常见的模式是定义一个您可以在两个文件中导入和使用的函数。
以下是如何在 Gatsby SSR 和 Gatsby Browser 中使用 wrapPageElement 的示例,而无需复制代码
onLoad 和 onError 回调
使用 post-hydrate 或 idle 策略加载的带源脚本可以访问两个回调
onLoad- 脚本加载后调用onError- 如果脚本加载失败则调用
注意 - 内联脚本和使用
off-main-thread策略的脚本 不 支持onLoad和onError回调。
以下是使用回调的示例
重复的脚本(具有相同 id 或 src 属性的脚本)将执行 onLoad 和 onError 回调,尽管它们未注入 DOM。
加载脚本依赖项
onLoad 和 onError 回调的访问也使得能够加载依赖脚本。以下是一个示例,展示了如何加载第一个脚本之后的第二个脚本