针对更复杂网站实现推测规则的指南

发布时间:2025 年 3 月 7 日

借助 Speculation Rules API,用户可以预提取或预渲染未来的网页导航,从而提升性能,实现更快甚至即时的网页导航。

此 API 的设计专为易于实现而打造,但复杂网站在使用前需要特别注意一些事项。本指南可帮助网站所有者了解这些注意事项。

规划

三个阶段:规划、实施和衡量,其中突出显示了“规划”。

在实现推测规则之前,不妨考虑如何实现 API(因为有几个选项),以及推测的成本(这应该有助于您确定要推测哪些网页)。

确定如何实现推测规则

您需要做出的第一个决定是如何在您的网站上实现投机规则,因为您可以使用多种方法:

  • 直接在网页的 HTML 中
  • 使用 JavaScript
  • 使用 HTTP 标头

最终,每种方法都会产生相同的效果,但在易于实现和灵活性方面可能有优势。

网站应选择最适合自己的选项,甚至可以根据需要组合使用这些选项。或者,您也可以使用插件(例如适用于 WordPress 的推测性加载插件)或库或平台来实现这些功能,这些插件或平台可能会为您做出选择,但了解可用的选项仍然很有价值。

直接在网页的 HTML 中添加推测规则

您可以通过在 HTML 中添加 <script type="speculationrules"> 元素,直接在网页上实现推测规则。对于使用模板的静态网站,可以在构建时添加此标记;对于动态网站,可以在服务器收到网页请求时在运行时添加此标记。边缘工作器甚至可以将它们注入 HTML 中(不过,本指南稍后将介绍的 HTTP 标头方法可能更为简单)。

这样,您就可以在整个网站中添加静态规则,但文档规则仍然可以是动态的,因为您可以使用由 CSS 类触发的规则来选择要从网页中呈现的网址:

<script type="speculationrules">
  {
    "prerender": [{
      "where": { "selector_matches": ".prerender" }
    }],
    "prefetch": [{
      "where": { "selector_matches": ".prefetch" }
    }]
  }
</script>

上一个脚本会预渲染具有 prerender 类的链接,并且当链接具有 prefetch 类时,也会进行预提取。这样,开发者就可以在 HTML 中添加这些类来触发推测。

除了在页面的初始 HTML 中添加这些类的链接之外,如果这些类由您的应用动态添加,系统还会推测链接,以便您的应用根据需要触发(和移除)推测。这可能比创建或移除更具体的推测规则更简单。如果您希望为大多数网站使用一个基本规则,并为特定网页使用特定规则,还可以为每个网页添加多个推测规则

或者,如果您确实需要使用更具体的推测规则,则可以使用针对特定网页或模板的规则,为特定网页或网页类型设置不同的规则。

最后,服务器端呈现的网页还可以根据服务器可用的任何信息(例如相应网页的分析信息或特定网页的常见用户历程)采用更动态的规则。

使用 JavaScript 添加推测规则

将规则添加到页面脚本中的替代方法是使用 JavaScript 注入规则。这样一来,您可能就不需要频繁更新页面模板。例如,让跟踪代码管理器注入规则是一种快速发布推测规则的方式(还可以在需要时快速关闭这些规则)。

此选项还允许根据用户与网页的互动方式使用动态客户端规则。例如,如果用户将商品添加到购物车,您可以预渲染结账页。或者,您也可以根据特定条件触发推测。虽然该 API 包含允许使用基本互动规则的提前设置,但 JavaScript 允许开发者使用自己的逻辑来决定何时以及在哪些网页上进行推测。

如前所述,插入新规则的另一种方法是在网页上设置基本文档规则,并通过向链接添加相应类来让 JavaScript 触发文档规则,从而使链接与规则匹配。

使用 HTTP 标头添加推测规则

开发者最后一个选项是使用 HTTP 标头添加规则:

Speculation-Rules: "/speculationrules.json"

关于规则资源(在本例中为 /speculationrules.json)的提交和使用方式,有一些额外的要求

借助此选项,CDN 可以更轻松地进行部署,而无需更改文档内容。这确实意味着,无法使用 JavaScript 动态更改推测规则。不过,使用 CSS 选择器触发器的文档规则仍然可以允许进行动态更改,例如从链接中移除 prerender 类。

与 JavaScript 选项类似,使用 HTTP 标头实现推测规则可让这些规则独立于网站内容实现,从而更轻松地添加和移除规则,而无需完全重建网站。

考虑费用影响

在实施投机规则之前,不妨花些时间考虑使用此 API 对用户和您的网站产生的费用影响。费用包括带宽费用(会给用户和网站带来开销!)和处理费用(客户端和服务器端)。

考虑用户的费用

推测性加载是指根据用户可能导航到的新位置进行有根据的猜测。不过,如果未执行此导航,则可能会浪费资源。因此,您应注意对用户的影响,尤其是:

  • 用于下载这些未来导航信息的额外带宽,尤其是在带宽可能更受限的移动设备上。
  • 使用预渲染时,渲染这些网页的额外处理开销。

如果预测完全准确,则无需额外付费,因为访问者接下来会访问这些网页,唯一的区别是这些费用会提前支付。不过,我们不可能完全准确地预测未来,而且投机策略越激进,浪费的风险就越高。

Chrome 已仔细考虑了这个问题,该 API 包含多项功能,这意味着费用比您想象的要低得多。特别是,通过重复使用 HTTP 缓存,而不是加载没有缓存资源的完整网页,在同一网站上预渲染导航的成本通常要比没有缓存资源的完整网页小得多,这使得推测性加载的成本低于预期。

不过,即使采取了这些保护措施,网站也应仔细考虑要推测哪些网页,以及此类推测给用户带来的成本。适合推测性加载的候选项包括:可合理预测且具有较高置信度(可能基于分析数据或常见用户转化历程),并且成本较低(例如,页面内容较少)的项。

您可能还需要考虑在激活之前应延迟哪些 JavaScript。这与在需要时才延迟加载内容类似,可以降低预渲染的费用,但能提供更好的用户体验。由于推理费用更低,您或许可以更频繁或更积极地推理。

如果无法做到这一点,建议使用温和或保守的热情规则,采用不那么激进的策略。或者,您也可以使用预提取,在信心较低时,预提取的费用要比预渲染低得多,然后在信心提高时(例如,用户悬停在链接上或实际点击链接时)升级为完整预渲染。

考虑额外的后端负载

除了考虑用户的额外费用外,网站所有者还应考虑自己的基础架构费用。如果每个网页都会导致两次、三次甚至更多次网页加载,那么使用此 API 可能会增加后端费用。

确保您的网页和资源可缓存,将显著减少源站加载量,从而降低总体风险。与 CDN 搭配使用时,您的源服务器应该会承受最少的额外负载,但请考虑任何 CDN 费用增加。

服务器或 CDN 还可用于控制由安全用途 HTTP 标头标识的推测结果。例如,Cloudflare 的 Speed Brain 产品仅允许在 CDN 边缘服务器上缓存的推测,并且不会将请求发回给源。

不过,由于推测性加载通常用于同源网页加载,因此用户的浏览器缓存中已经有共享资源(假设这些资源一开始就是可缓存的),因此再次强调,推测性加载通常不会像完整网页加载那样耗费资源。

在过度推测和过少推测之间取得平衡

要充分利用 Speculation Rules API,关键在于在过度推测(即支付不必要的费用且推测未被使用)和过于保守(推测过少或过晚,无法获得太多好处)之间取得平衡。

在费用较低的情况下(例如,在 CDN 边缘节点上缓存的小型静态生成的网页),您可以更积极地进行推测。

不过,对于可能无法在 CDN 边缘缓存的更大、更丰富的网页,则应多加注意。同样,资源密集型网页可能会耗尽网络带宽或处理能力,从而对当前网页产生负面影响。API 的目标是提高性能,因此性能回归绝不是我们想要的!这也是将预渲染网页数量限制为最多一两个网页的另一个原因(另请注意,Chrome 一次最多限制为预渲染两到十个网页,具体取决于提前性)。

实现推测规则的步骤

三个阶段:计划、实施和衡量,其中突出显示了“实施”阶段。

确定如何实现推测规则后,您接下来需要规划要推测的内容以及如何进行推测。结构较简单的网站(例如静态个人博客)或许可以直接跳转到对某些网页进行完整预渲染,但对于结构较为复杂的网站,则需要考虑更多复杂性。

从预加载开始

预提取通常对大多数网站来说都相对安全,这是许多网站(包括 CloudflareWordPress 等大规模部署)采用的初始方法。

需要注意的主要问题是,预提取网址是否会导致任何状态更改和服务器端开销,尤其是对于无法缓存的网页。理想情况下,状态更改(例如预加载 /logout 页面)不应作为 GET 链接实现,但遗憾的是,这种情况在 Web 上并不少见。

您可以从规则中明确排除以下网址:

<script type="speculationrules">
  {
    "prefetch": [{
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": {"href_matches": "/logout"}}
        ]
      },
      "eagerness": "moderate"
    }]
  }
</script>

您可以使用 moderateconservative eagerness 设置,将预提取限制为从一个网页导航到另一个网页的常见导航,或者在用户悬停或点击时针对所有同源链接进行预提取。conservative 设置的风险最低,但潜在回报也最低。如果从 1.6 开始,则至少应升级到 moderate,但最好升级到 eager,这样可以获得更大的性能优势(如果适用,还可以进一步升级到 prerender)。

低风险预渲染

预提取推测更易于部署,但 API 的最终性能优势在于预渲染。如果在推测后不久未访问网页,则可能需要考虑一些额外事项(我们将在下一部分中介绍),但如果使用 moderateconservative 预渲染,则在推测后不久发生导航可能是一种风险相对较低的后续步骤。

<script type="speculationrules">
  {
    "prerender": [{
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": {"href_matches": "/logout"}}
        ]
      },
      "eagerness": "moderate"
    }]
  }
</script>

预提取常见网页以改进非提前预渲染

一种常见策略是在加载时使用 eager 设置预提取少量经常访问的下一页(通过在网址列表中指定它们或使用 selector_matches),然后使用 moderate 设置预渲染。由于 HTML 预提取操作可能会在用户悬停链接时完成,因此与仅在用户悬停时进行预渲染而无需预提取相比,这种做法可以显著提升性能。

<script type="speculationrules">
  {
    "prefetch": [{
      "urls": ["next.html", "next2.html"],
      "eagerness": "eager"
    }],
    "prerender": [{
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": {"href_matches": "/logout"}}
        ]
      },
      "eagerness": "moderate"
    }]
  }
</script>

更早的预渲染

虽然 moderate 文档规则允许以相对较低的风险使用 API,并且实现起来也相对容易,但这通常不足以进行完整的预渲染。如需实现此 API 支持的即时导航,您可能需要采取更进一步的措施,更积极地预渲染页面。

为此,您可以使用静态网址列表(如前面的预提取示例),也可以使用 selector_matches 来识别少量网址(最好是 1 到 2 个网页),并使用文档规则来涵盖其他网址:

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "selector_matches": : ".prerender"
        },
        "eagerness": "eager",
      },
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": {"href_matches": "/logout"}}
          ]
        },
        "eagerness": "moderate"
      }
    ]
  }
</script>

这可能需要进行流量分析,以便尽可能准确地预测下一次导航。了解客户在您网站上的典型转化历程也有助于确定适合推测性加载的候选资源。

改用更积极的预渲染功能可能还会带来更多与分析、广告和 JavaScript 相关的注意事项,以及保持预渲染页面最新状态的需要,甚至需要取消或刷新状态更改的推测

Google Analytics、广告和 JavaScript

使用预渲染功能时,更复杂的网站还必须考虑对 Google Analytics 的影响。通常,您不希望在网页被推测时记录网页(或广告)浏览,而只希望在推测启用后记录。

某些分析服务提供商(例如 Google Analytics [分析])和广告提供商(例如 Google 发布商代码)已支持推测规则,并且在网页激活之前不会记录浏览。不过,您已实现的其他提供商或自定义分析服务可能需要额外考虑。

您可以向 JavaScript 添加检查,以防止在网页激活或显示之前执行特定代码段,甚至可以将整个 <script> 元素封装在此类检查中。如果网页使用跟踪代码管理器注入此类脚本,则可以通过延迟跟踪代码管理器脚本本身来一次性解决所有问题。

同样,意见征求管理平台可让您延迟第三方脚本的加载,直到用户启用意见征求。Google 一直在与各种意见征求管理平台合作,让它们支持预渲染,我们也非常乐意帮助其他希望实现此功能的平台。PubTech 就是这样一家公司,可让开发者选择在预渲染期间运行或屏蔽其 JavaScript

对于应用代码,您也可以同样添加更改,以延迟代码执行到激活,尤其是在网页不需要 JavaScript 代码呈现的情况下。这是一种更快、更安全的选项,但这意味着所有代码都会在激活时同时运行。这可能会导致激活时需要执行大量工作,进而影响 INP,尤其是在网页看起来已完全加载且可以与之互动时。

此外,如果任何内容都依赖于 JavaScript(例如客户端呈现的内容),延迟此操作会降低预渲染对 LCPCLS 的积极影响。采用更具针对性的方法,允许在预渲染阶段运行更多 JavaScript,可获得更好的体验,但实现起来可能并不简单。

对于更复杂的网站,一开始完全延迟许多脚本标记是一个不错的策略。不过,为了充分利用该 API,最终目标是允许在预渲染期间运行尽可能多的 JavaScript。

如果网站存在分析或广告问题,不妨先从预提取开始,因为这些问题在预提取中不太重要,同时考虑需要执行哪些操作来支持预渲染。

更新预渲染推测

在导航之前预渲染网页时,预渲染的网页可能会过时。例如,电子商务网站上的预渲染页面可能包含结账购物篮,即包含商品的完整购物篮,甚至可能只是在其他页面上显示购物篮中商品数量的计数器。如果向购物车中添加了更多商品,然后转到预渲染的页面,用户看到旧的结账状态会感到困惑。

这并不是一个新问题,当用户在浏览器中打开多个标签页时,也会遇到同样的问题。不过,对于预渲染的网页,这种情况更有可能发生,并且更出乎意料,因为用户并未有意启动预渲染。

Broadcast Channel API 是一种允许浏览器中的某个网页向其他网页广播此类更新的方法。这也可以解决多个标签页问题。预渲染页面可以监听广播消息,但在激活之前无法发送自己的广播消息。

或者,预渲染的网页也可以使用服务器(使用周期性 fetch()WebSocket 连接)获取更新,但更新可能会出现延迟。

取消或刷新预渲染推测

如需继续使用预呈现页面,同时避免让用户感到困惑,建议更新预呈现页面。如果无法做到这一点,则可以取消推测。

如果网站想要预渲染更有可能被访问的其他网页,也可以使用此方法来确保不会超出 Chrome 的限制

如需取消推测,您需要从网页中移除推测规则,或者移除类或其他匹配条件(如果使用该方法)。或者,如果推测的页面检测到自己已不再是当前页面,则可以调用 window.close()。不过,如果网页能够检测到这种情况,更好的做法是更新其状态,使其保持最新状态。

您还可以重新插入这些规则(或匹配条件),以便重新预渲染网页(不过,再次强调,通常最好让现有网页保持最新状态,因为这样可以减少浪费)。移除推测规则后,必须在新微任务中或之后完成重新插入,以便浏览器注意到移除操作并取消推测。以下示例展示了一种删除和移除所有推测规则脚本的方法:

async function refreshSpeculations() {
  const speculationScripts = document.querySelectorAll('script[type="speculationrules"]');

  for (const speculationScript of speculationScripts) {
    // Get the current rules as JSON text
    const ruleSet = speculationScript.textContent;

    // Remove the existing script to reset prerendering
    speculationScript.remove();
    
    // Await for a microtask before re-inserting.
    await Promise.resolve();

    // Reinsert rule in a new speculation rules script
    const newScript = document.createElement('script');
    newScript.type = 'speculationrules';
    newScript.textContent = ruleSet;
    console.log(newScript);

    // Append the new script back to the document
    document.body.appendChild(newScript);
  }
}

移除规则会取消现有预取器(或预提取),但重新插入规则只会推测立即或提前规则(包括使用立即作为默认值的网址列表规则)。不过,系统会移除“一般”或“暂缓”的推测,但不会自动重新触发,除非用户再次与相应链接互动。

此刷新选项不限于 JavaScript 插入的规则。由于这是一种标准的 DOM 更改,因此 HTML 中包含的静态规则也可以以相同的方式移除或更改。您无法移除 HTTP 推测规则,但可以移除匹配条件(例如 prerender 类),并通过 JavaScript 重新添加。

Chrome 还在考虑添加 Clear-Site-Header 支持,以允许服务器响应取消预渲染(例如,在发出更新购物车请求时)。

衡量成效

三个阶段:规划、实施、衡量

实现推测规则后,您应衡量效果,而不要仅仅假定速度会自动加快。如前所述,如果客户端或服务器过载,过度推测实际上可能会导致性能回归。

在通过多个步骤(预提取、低风险预渲染,然后是提前预渲染)实现时,您应针对每个步骤进行衡量。

如何衡量成效

推测规则应该会对 LCP 等关键性能指标(可能还包括 CLS 和 INP)产生积极影响,但这些影响在网站级整体指标中可能并不明显。这是因为网站可能主要由其他导航类型(例如着陆页)组成,或者因为同源导航速度已经足够快,即使大幅提升其速度,也可能不会影响 Chrome 用户体验报告 (CrUX) 中报告的第 75 个百分位数指标。

您可以使用 CrUX 中的页面导航类型来检查导航中有多少百分比是 navigate_cacheprerender,以及这一比例是否会随时间推移而增加。不过,如需进行详细分析,您可能需要使用“真实用户监控”功能将数据细分为推测性导航,以了解这些导航速度比其他导航快了多少。

如何衡量使用量和浪费量

另一个关键考虑因素是衡量您是否在对正确的网页进行推测。这样既能避免浪费,又能确保您定位到最适合利用此 API 的网页。

很遗憾,发起推测的页面无法直接查看推测尝试的状态。此外,由于浏览器可能会在某些情况下抑制推测,因此无法假定已触发尝试。因此,必须在网页本身上衡量这些指标。这还需要检查两个 API,以确定网页是否正在推测或是否已推测:

if (document.prerendering) {
  console.log("Page is prerendering");
} else if (performance.getEntriesByType("navigation")[0]?.activationStart > 0) {
  console.log("Page has already prerendered");
} else {
  console.log("This page load was not using prerendering");
}

然后,此页面可以将推测尝试记录到后端服务器。

分析的一个复杂之处在于,一些提供商(例如 Google Analytics [分析])支持预渲染,并且会在页面激活之前忽略分析调用,即使是单独的事件调用也是如此。因此,Google Analytics 用户必须使用另一个选项:服务器端日志记录选项。

也可以在客户端执行此操作,每个预渲染的网页都会在共享存储空间中记录预渲染,然后调用网页会读取这些信息。localStorage 最适合,因为它可以在离开网页时读取(请注意,由于 sessionStorage 对预渲染的网页有特殊处理,因此无法使用)。不过,请注意,localStorage 不具有事务安全性,如果预渲染多个网页,其他网页可能会同时更新此值。此演示使用唯一的哈希和单独的条目来避免此类问题。

总结

推测规则有可能显著提升页面性能。本指南提供了有关实现此 API 时需要注意的事项的建议,以避免任何潜在问题,并充分利用此 API。

提前规划实施工作有助于避免返工。特别是对于更复杂的网站,应采用多步式发布流程,先从预提取开始,然后再进行低风险预渲染,最后进行提前预渲染。最后,请务必衡量改进情况以及任何使用情况和浪费情况,以确保您充分利用 API。