推測規則 API

實驗性: 這是一項實驗性技術
在生產中使用此技術之前,請仔細檢查瀏覽器相容性表格

推測規則 API 旨在提高未來導航的效能。它針對文件 URL 而不是特定的資原始檔,因此適用於多頁面應用程式(MPA)而不是單頁面應用程式(SPA)。

推測規則 API 提供了廣泛可用的 <link rel="prefetch"> 功能的替代方案,旨在取代僅限 Chrome 且已棄用的 <link rel="prerender"> 功能。它在這些技術上提供了許多改進,同時提供了更具表現力、可配置的語法,用於指定哪些文件應被預取或預渲染。

注意: 推測規則 API 不處理子資源預取;為此你需要使用 <link rel="prefetch">

概念與用法

推測規則可以在內聯 <script type="speculationrules"> 元素和由 Speculation-Rules 響應頭引用的外部文字檔案中指定。規則以 JSON 結構指定。

指令碼示例

html
<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": { "href_matches": "/logout" } },
            { "not": { "href_matches": "/*\\?*(^|&)add-to-cart=*" } },
            { "not": { "selector_matches": ".no-prerender" } },
            { "not": { "selector_matches": "[rel~=nofollow]" } }
          ]
        }
      }
    ],
    "prefetch": [
      {
        "urls": ["next.html", "next2.html"],
        "requires": ["anonymous-client-ip-when-cross-origin"],
        "referrer_policy": "no-referrer"
      }
    ]
  }
</script>

如果網站包含 Content-Security-Policyscript-src 指令,則需要明確允許使用 <script> 元素進行的推測規則。這可以透過新增 'inline-speculation-rules' 源、雜湊源或 nonce 源之一來完成。

HTTP 頭示例

http
Speculation-Rules: "/rules/prefetch.json"

包含推測規則 JSON 的文字資源可以有任何有效的名稱和副檔名,但它必須以 application/speculationrules+json MIME 型別提供。

注意: 規則可以同時使用內聯指令碼和 HTTP 頭指定 — 應用於文件的所有規則都會被解析並新增到文件的推測規則列表中。

你為每種推測性載入型別(例如 "prerender""prefetch")指定一個不同的陣列來包含規則。每個規則都包含在一個物件中,該物件例如指定要獲取的資源列表,以及每個規則的選項,例如明確的 Referrer-Policy 設定。請注意,預渲染的 URL 也會被預取。

有關可用語法的完整說明,請參閱 <script type="speculationrules">

使用預取

<script type="speculationrules"> 元素或 Speculation-Rules 頭中包含 prefetch 規則將使支援的瀏覽器下載引用的頁面的響應正文,但不會下載頁面引用的任何子資源。當導航到預取頁面時,它將比未預取時渲染得更快。

結果儲存在每個文件的記憶體快取中。當你離開當前頁面時,所有快取的預取都將被丟棄,當然除了你隨後導航到的預取文件。

這意味著如果你預取了使用者沒有導航到的內容,通常會浪費資源,儘管如果頭部允許,結果可能會填充 HTTP 快取。話雖如此,預取的前期成本遠小於預渲染的前期成本,因此我們鼓勵你廣泛採用預取,例如預取你網站上的所有重要頁面,前提是它們可以安全預取(有關更多詳細資訊,請參閱不安全的推測性載入條件)。

同站和跨站預取都將起作用,但跨站預取受到限制(有關兩者之間差異的解釋,請參閱“同站”和“跨站”)。出於隱私原因,跨站預取目前僅在使用者未為目標站點設定 cookie 的情況下才有效 — 我們不希望站點能夠透過預取頁面(他們甚至可能從未實際訪問過)來跟蹤使用者活動,基於先前設定的 cookie。

注意: 將來將透過 Supports-Loading-Mode 頭提供跨站預取的選擇加入,但在撰寫本文時尚未實現(僅提供了跨域、同站預渲染的選擇加入)。

對於支援它的瀏覽器,應優先使用推測規則預取,而不是較舊的預取機制,即 <link rel="prefetch">fetch(),其中設定了 priority: "low" 選項。因為我們知道推測規則預取是用於導航的,而不是一般的資源預取。

  • 它可用於跨站導航,而 <link rel="prefetch"> 不能。
  • 它不會被 Cache-Control 頭阻止,而 <link rel="prefetch"> 經常會被阻止。

此外,推測規則預取

  • 在需要時自動降低優先順序(fetch() 不會)。
  • 尊重使用者的配置。例如,當用戶的裝置處於省電模式或資料節省模式時,不會發生預取。
  • 將預取資源儲存在每個文件的記憶體快取中,而不是 HTTP 快取中,這可能會導致稍微更快的預取。

使用預渲染

<script type="speculationrules"> 元素或 Speculation-Rules 頭中包含 prerender 規則將使支援的瀏覽器獲取、渲染並載入內容到一個不可見的標籤頁中,儲存在每個文件的記憶體快取中。這包括載入所有子資源,執行所有 JavaScript,甚至載入子資源和執行由 JavaScript 啟動的資料獲取。當你離開當前頁面時,所有快取的預渲染及其子資源都將被丟棄,當然除了你隨後導航到的預渲染文件。

未來導航到預渲染頁面將幾乎是即時的。瀏覽器會啟用不可見的標籤頁,而不是執行通常的導航過程,用預渲染頁面替換舊的前景頁面。如果頁面在完全預渲染之前被啟用,它將以其當前狀態啟用,然後繼續載入,這意味著你仍然會看到顯著的效能改進。

預渲染會消耗記憶體和網路頻寬。如果你預渲染了使用者沒有導航到的內容,這些資源就會被浪費(儘管如果頭部允許,結果可能會填充 HTTP 快取,從而允許以後使用)。預渲染的前期成本遠大於預取的前期成本,其他條件也可能導致內容不安全而無法預渲染(有關更多詳細資訊,請參閱不安全的推測性載入條件)。因此,我們鼓勵你更謹慎地採用預渲染,仔細考慮頁面被導航到的可能性很高的情況,並且你認為使用者體驗的收益值得額外的成本。

注意: 為了說明潛在資源浪費的程度,預渲染使用的資源量與渲染一個 <iframe> 大致相同。

注意: 許多 API 在預渲染期間/直到啟用之前將自動推遲。有關更多詳細資訊,請參閱預渲染期間推遲或受限的平臺功能

預渲染預設僅限於同源文件。跨域、同站預渲染是可能的 — 它需要導航目標使用 Supports-Loading-Mode 頭並設定為 credentialed-prerender 值來選擇加入。目前無法進行跨站預渲染。

對於支援它的瀏覽器,應優先使用推測規則預渲染,而不是較舊的預渲染機制,即 <link rel="prerender">

  • <link rel="prerender"> 是 Chrome 特有的,從未標準化,Chrome 工程團隊正在逐步淘汰它。
  • 它載入透過 JavaScript 載入的子資源,而 <link rel="prerender"> 不會。
  • 它不會被 Cache-Control 設定阻止,而 <link rel="prerender"> 經常會被阻止。
  • 推測規則預渲染應被視為一種提示和漸進增強。與 <link rel="prerender"> 不同,它是一種推測性提示,瀏覽器可能會根據使用者設定、當前記憶體使用情況或其他啟發式方法選擇不執行該提示。

推測規則 API 功能檢測

你可以使用以下程式碼檢查是否支援推測規則 API:

js
if (
  HTMLScriptElement.supports &&
  HTMLScriptElement.supports("speculationrules")
) {
  console.log("Your browser supports the Speculation Rules API.");
}

例如,你可能希望在支援的瀏覽器中插入用於預取的推測規則,但在其他瀏覽器中使用較舊的技術,例如 <link rel="prefetch">

js
if (
  HTMLScriptElement.supports &&
  HTMLScriptElement.supports("speculationrules")
) {
  const specScript = document.createElement("script");
  specScript.type = "speculationrules";
  const specRules = {
    prefetch: [
      {
        source: "list",
        urls: ["/next.html"],
      },
    ],
  };
  specScript.textContent = JSON.stringify(specRules);
  document.body.append(specScript);
} else {
  const linkElem = document.createElement("link");
  linkElem.rel = "prefetch";
  linkElem.href = "/next.html";
  document.head.append(linkElem);
}

檢測預取和預渲染的頁面

本節介紹檢測請求頁面是否已預取或預渲染的不同方法。

伺服器端檢測

預取和預渲染的頁面請求會發送 Sec-Purpose 請求頭:

用於預取

http
Sec-Purpose: prefetch

用於預渲染

http
Sec-Purpose: prefetch;prerender

伺服器可以根據此頭進行響應,例如,記錄推測性載入請求,返回不同的內容,甚至阻止推測性載入發生。如果返回非成功響應程式碼(重定向後 HTTP 狀態碼不在 200-299 範圍內),則頁面將不會被預取/預渲染。此外,204 和 205 狀態碼也會阻止預渲染(但不會阻止預取)。

使用非成功程式碼(例如 503)是阻止伺服器端推測性載入的最簡單方法,儘管通常更好的方法是允許預取/預渲染,並使用 JavaScript 延遲任何應僅在實際檢視頁面時才發生的動作。

JavaScript 預取檢測

當頁面被預取時,其 PerformanceResourceTiming.deliveryType 條目將返回 "navigational-prefetch" 值。你可以使用以下程式碼在收到型別為 "navigational-prefetch" 的效能條目時執行函式:

js
if (
  performance.getEntriesByType("navigation")[0].deliveryType ===
  "navigational-prefetch"
) {
  respondToPrefetch(); // Author-defined function
}

當測量效能時,或者當你想推遲可能在預取期間導致問題的操作時(參見不安全的預取),此技術很有用。

JavaScript 預渲染檢測

要在頁面預渲染時執行活動,你可以檢查 Document.prerendering 屬性。例如,你可以執行一些分析:

js
if (document.prerendering) {
  analytics.sendInfo("got this far during prerendering!");
}

當預渲染文件被啟用時,PerformanceNavigationTiming.activationStart 被設定為一個 DOMHighResTimeStamp,表示預渲染開始到文件啟用之間的時間。以下函式可以檢查預渲染預渲染頁面:

js
function pagePrerendered() {
  return (
    document.prerendering ||
    self.performance?.getEntriesByType?.("navigation")[0]?.activationStart > 0
  );
}

當用戶檢視頁面啟用預渲染頁面時,將觸發 prerenderingchange 事件。這可以用於啟用那些原本在頁面載入時預設啟動,但你希望延遲到使用者檢視頁面時才啟動的活動。以下程式碼設定了一個事件監聽器,用於在預渲染完成時(在預渲染頁面上)執行一個函式,或者在非預渲染頁面上立即執行它:

js
if (document.prerendering) {
  document.addEventListener("prerenderingchange", initAnalytics, {
    once: true,
  });
} else {
  initAnalytics();
}

不安全的推測性載入條件

本節涵蓋了需要注意的條件,在這些條件下預取和/或預渲染是不安全的。這意味著預取/預渲染呈現這些條件的頁面可能需要在你的程式碼中進行緩解,或者需要完全避免。

不安全的預取

如前所述,我們建議廣泛採用預取,因為風險與收益之比相當低——資源浪費的潛力很小,而效能提升可能很大。但是,你需要確保預取頁面不會對應用程式的流程造成問題。

當執行預取時,瀏覽器透過單個 GET 請求下載引用頁面的響應正文,使用者可能在未來某個時間導航到該頁面。問題可能特別出現在請求的 URL 執行伺服器端副作用時,而你希望這些副作用直到導航到該 URL 時才發生。

例如

  • 退出 URL。
  • 語言切換 URL。
  • “新增到購物車”URL。
  • 登入流程 URL,其中伺服器導致傳送簡訊,例如作為一次性密碼(OTP)。
  • 增加使用者使用額度的 URL,例如消耗其每月免費文章額度或啟動其每月分鐘計時器。
  • 啟動伺服器端廣告轉化跟蹤的 URL。

此類問題可以在伺服器端透過監聽傳入請求中的 Sec-Purpose: prefetch 頭來緩解,然後執行特定程式碼來延遲有問題的功能。稍後,當實際導航到頁面時,如果需要,你可以透過 JavaScript 啟動延遲的功能。

注意: 有關檢測程式碼的更多詳細資訊,請參閱檢測預取和預渲染頁面部分。

預取一個伺服器渲染內容會因使用者在當前頁面上採取的操作而改變的文件也存在潛在風險。這可能包括例如限時搶購頁面或電影院座點陣圖。仔細測試此類情況,並透過在頁面載入後更新內容來緩解此類問題。有關這些情況的更多詳細資訊,請參閱伺服器渲染的可變狀態

注意: 瀏覽器會將預取頁面快取一小段時間(例如 Chrome 快取 5 分鐘),然後丟棄它們,因此在任何情況下,你的使用者都可能會看到最多 5 分鐘過時的內容。

可以使用 Clear-Site-Data 響應頭的 prefetchCache 值清除過期的預取。例如,當狀態更改請求導致快取資料不再有效時(例如退出網站時),可以使用此方法。

如果獲取頁面的所有副作用都來自 JavaScript 執行,那麼預取是安全的,因為 JavaScript 在啟用之前不會執行。

最後一個提示是稽核你 robots.txt 檔案中列為不允許的 URL —— 通常這些 URL 指向只有經過身份驗證的使用者才能訪問的頁面,因此不應包含在搜尋引擎結果中。其中許多 URL 都沒有問題,但這可能是一個尋找不安全預取 URL(即,它們表現出上述條件)的好地方。

不安全的預渲染

預渲染比預取更具風險,因此應謹慎使用,僅在值得的情況下進行。預渲染需要注意更多不安全的條件,因此,儘管回報更高,風險也更大。

當完成預渲染時,瀏覽器會獲取 URL 並將內容渲染並載入到不可見的選項卡中。這包括執行內容的 JavaScript 並載入所有子資源,包括透過 JavaScript 獲取的資源。如果觀察到以下任何條件,則內容可能不安全而無法預渲染:

  • 該 URL 不安全以進行預取。如果你尚未閱讀,請先閱讀上一節,並瞭解這些條件同樣適用於不安全的預渲染。
  • 頁面載入時,其 JavaScript 以可能導致其他未預渲染頁面(使用者當前正在檢視的頁面)中出現混淆效果的方式修改客戶端儲存(例如 Web 儲存IndexedDB)。
  • 頁面執行 JavaScript 或載入影像,導致副作用,例如傳送分析、記錄廣告印象,或以使用者已經與應用程式互動的方式修改應用程式的狀態。同樣,這可能會影響應用程式的流程,或導致不正確的效能或使用報告。有關此類用例的更多詳細資訊,請參閱伺服器渲染的可變狀態

為了緩解此類問題,你可以使用以下技術:

  • 在伺服器端,當請求傳入時,監聽 Sec-Purpose: prefetch 頭,然後執行特定程式碼以推遲有問題的功能。
  • 使用 prerenderingchange 事件來檢測預渲染頁面何時實際被啟用並執行程式碼。這在兩種情況下很有用:
    • 推遲可能在頁面被檢視之前執行會導致問題的程式碼。例如,你可能希望等到啟用後才更新客戶端儲存或使用 JavaScript 修改伺服器端狀態。這可以避免 UI 和應用程式狀態相互不同步的情況,例如購物車顯示沒有商品,即使使用者已經添加了一些商品。
    • 如果上述方法不可行,那麼你仍然可以在頁面啟用後重新執行程式碼以使應用程式再次保持最新。例如,一個高度動態的限時搶購頁面可能依賴於來自第三方庫的內容更新。如果你無法延遲更新,你總可以在使用者檢視頁面後獲取最新更新。預渲染頁面可以使用 Broadcast Channel API 或其他機制(如 fetch()WebSocket)進行即時更新。這保證了使用者在預渲染啟用後將看到最新的內容。
  • 仔細管理你的第三方分析指令碼 — 如果可能,請使用具有預渲染感知功能的指令碼(例如,使用 Document.prerendering 屬性來延遲在預渲染頁面上執行),例如 Google Analytics 或 NewRelic。
    • 請注意,跨域 <iframe> 的內容載入在預渲染期間會被延遲,直到啟用發生。這樣做是為了避免載入未感知預渲染的跨域頁面導致的故障,並避免圍繞向這些框架公開哪些憑據和儲存的複雜性。這意味著使用者在某些情況下最初可能會看到空白框架,但這也意味著大多數第三方小部件(例如廣告技術)在預渲染期間可以安全使用。
    • 對於不具備預渲染感知功能的第三方指令碼,如前所述,避免在啟用之前載入它們,使用 prerenderingchange 事件。

伺服器渲染的可變狀態

需要關注的伺服器渲染狀態主要有兩種:過時狀態使用者特定狀態。這可能導致不安全的預取和預渲染。

  • 過時狀態:考慮一個伺服器渲染的部落格評論列表的例子,它可能在部落格文章被預渲染和被檢視之間變得過時。如果當前頁面是一個管理員面板,使用者正在刪除垃圾評論,這可能尤其成問題。如果使用者隨後導航到部落格文章,他們可能會對為什麼能看到剛剛刪除的垃圾評論感到困惑。
  • 使用者特定狀態:考慮透過 cookie 跟蹤登入狀態的示例。可能會出現以下問題:
    • 使用者在選項卡 1 中訪問 https://site.example/a,在選項卡 2 中訪問 https://site.example/b,此時處於登出狀態。
    • https://site.example/b 預渲染 https://site.example/c。它將以登出狀態預渲染。
    • 使用者在選項卡 1 中登入 https://site.example
    • 使用者切換到選項卡 2 並點選連結到 https://site.example/c,這將啟用預渲染頁面。
    • 選項卡 2 顯示 https://site.example/c 的登出檢視,這讓使用者感到困惑,因為他們以為自己已登入。

使用者特定狀態問題也可能發生在其他使用者設定上,例如語言設定、深色模式偏好或向購物車新增商品。它們也可能發生在只涉及一個選項卡時:

  • 假設使用者訪問 https://site.example/product
  • https://site.example.com/product 預渲染 https://site.example.com/cart。它預渲染時購物車中有 0 件商品。
  • 使用者點選“新增到購物車”按鈕,這會發起一個獲取請求,將商品新增到使用者的購物車(不重新載入頁面)。
  • 使用者點選連結到 https://site.example.com/cart,這將啟用預渲染頁面。
  • 使用者看到一個空的購物車,儘管他們剛剛添加了一些東西。

對於這些情況,以及任何內容可能與伺服器不同步的情況,最好的緩解措施是讓頁面根據需要自行重新整理。例如,伺服器可以使用 Broadcast Channel API 或其他機制,如 fetch()WebSocket。頁面可以隨後適當地更新自己,包括尚未啟用的推測性載入頁面。

如果無法重新整理,可以使用 Clear-Site-Data 響應頭及其 prefetchCacheprerenderCache 值(或兩者),適當地清除推測。

該頭可以在任何同站 HTTP 請求(例如 /api/add-to-cart API 呼叫)中返回。

預渲染文件的會話歷史行為

從終端使用者的角度來看,啟用預渲染/已預渲染的文件與任何常規導航行為相似。啟用的文件會顯示在標籤頁中並附加到會話歷史記錄,任何現有的前進歷史記錄條目都會被修剪。在啟用之前在預渲染瀏覽上下文中發生的任何導航都不會影響會話歷史記錄。

從開發人員的角度來看,預渲染文件可以被視為具有微不足道的會話歷史記錄,其中只存在一個條目——當前條目。預渲染上下文中的所有導航實際上都被替換了。

雖然操作會話歷史記錄的 API 功能(例如 HistoryNavigation)可以在預渲染文件中呼叫,但它們只操作上下文的微不足道的會話歷史記錄。因此,預渲染文件不參與其引用頁面的聯合會話歷史記錄。例如,它們不能透過 History.back() 導航其引用者。

這種設計確保使用者在使用後退按鈕時獲得預期的體驗——即,他們會回到上次看到的內容。一旦預渲染文件被啟用,只有單個會話歷史記錄條目被附加到聯合會話歷史記錄中,忽略在預渲染瀏覽上下文內發生的任何先前導航。在聯合會話歷史記錄中後退一步——例如,透過按下後退按鈕——會將使用者帶回到引用頁面。

預渲染期間推遲或受限的平臺功能

由於預渲染頁面是在隱藏狀態下開啟的,因此一些可能導致侵入性行為的 API 功能在此狀態下不會被啟用,而是被推遲到頁面啟用之後。其他在預渲染時存在問題的 Web 平臺功能則完全受到限制。本節提供了哪些功能被推遲或受到限制的詳細資訊。

注意: 在少數無法推遲和限制的情況下,預渲染會被取消。

非同步 API 延遲

延遲意味著 API 功能會立即返回一個待處理的 Promise,然後什麼都不做,直到頁面啟用。啟用後,該功能正常執行,Promise 也正常解決或拒絕。

以下非同步功能的執行結果在預渲染文件中會被延遲,直到它們被啟用:

隱式受限的 API

以下功能在未啟用的文件中將自動失敗或不執行任何操作。

需要臨時啟用粘性啟用的 API。

需要包含文件獲得焦點的 API。

需要包含文件的 Document.visibilityState"visible" 的 API。

其他受限功能

  • 下載連結,即帶有 download 屬性的 <a><area> 元素,它們的下載將延遲到預渲染完成後。
  • 無跨站導航:任何導航到不同站點的預渲染文件將在請求傳送到該站點之前立即被丟棄。
  • 受限 URL:預渲染文件不能託管非 HTTP(S) 頂級 URL。包含以下 URL 型別將導致預渲染立即被丟棄:
  • 會話儲存:可以使用 Window.sessionStorage,但其行為非常特殊,以避免破壞那些期望只有一個頁面同時訪問標籤頁會話儲存的網站。因此,預渲染頁面在建立時會從標籤頁的會話儲存狀態克隆一份副本。啟用後,預渲染頁面的儲存副本將被丟棄,並改用標籤頁的主儲存狀態。使用會話儲存的頁面可以使用 prerenderingchange 事件來檢測儲存交換何時發生。
  • Window.print():任何對此方法的呼叫都將被忽略。
  • “簡單對話方塊方法”受到以下限制:
  • 專用/共享 worker 指令碼已載入,但其執行將延遲到預渲染文件被啟用。
  • 跨域 <iframe> 載入在預渲染期間會延遲,直到頁面啟用後。

介面

推測規則 API 不定義自己的任何介面。

其他介面的擴充套件

Document.prerendering 實驗性

一個布林屬性,如果文件當前正在預渲染過程中,則返回 true

prerenderingchange 事件 實驗性

當預渲染文件被啟用(即使用者檢視頁面)時,在該文件上觸發。

PerformanceNavigationTiming.activationStart 實驗性

一個數字,表示文件開始預渲染到啟用之間的時間。

PerformanceResourceTiming.deliveryType "navigational-prefetch" 實驗性

表示效能條目的型別是預取。

HTTP 標頭

Content-Security-Policy 'inline-speculation-rules' 實驗性

用於選擇允許使用 <script type="speculationrules"> 在被獲取的文件上定義推測規則。

Clear-Site-Data 'prefetchCache''prerenderCache' 實驗性

用於清除推測。例如,當狀態更改導致推測過時時。

Speculation-Rules 實驗性

提供一個指向包含推測規則 JSON 定義的文字資源 URL 列表。當響應是 HTML 文件時,這些規則將新增到文件的推測規則集中。

Supports-Loading-Mode 實驗性

由導航目標設定,用於選擇使用各種高風險載入模式。例如,跨域、同站預渲染需要 Supports-Loading-Mode 值為 credentialed-prerender

HTML 功能

<script type="speculationrules"> 實驗性

用於在當前文件內部定義一組預取和/或預渲染推測規則,這些規則將新增到文件的推測規則集中。

示例

有關程式碼示例,請參閱 developer.chrome.com 上的在 Chrome 中預渲染頁面以實現即時頁面導航 (2025)

規範

規範
HTML
# 推測性載入
預渲染改版

瀏覽器相容性

html.elements.script.type.speculationrules

http.headers.Speculation-Rules

另見

  • 有關推測規則和其他類似效能改進功能的比較,請參閱推測性載入