fetchLater() 配額

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

延遲的 fetchLater() API 請求會被批次處理,並在標籤頁關閉時傳送。此時,使用者沒有辦法中止這些請求。為了避免文件濫用頻寬傳送無限量資料,該 API 對可延遲傳送的資料量設定了配額。

這些配額可以透過 deferred-fetchdeferred-fetch-minimal 許可權策略 指令來管理。

概述

fetchLater() 的總配額為每個文件 640KiB。預設情況下,這被分為 512KiB 的頂級配額和 128KiB 的共享配額。

  • 預設情況下,512KiB 的頂級配額用於由頂級文件及其使用該源的直接子幀發起的任何 fetchLater() 請求。
  • 預設情況下,128KiB 的共享配額用於在跨源子幀(例如 <iframe><object><embed><frame> 元素)中發起的任何 fetchLater() 請求。

fetchLater() 請求可以傳送到任何 URL,不受限於文件或子幀的同源策略,因此區分在頂級文件內容中(無論是第一方還是第三方源)發起的請求和在子幀中發起的請求很重要。

例如,如果頂級文件 a.com 包含一個發出 fetchLater() 請求到 analytics.example.com<script>,該請求將受制於 512KiB 的頂級配額。或者,如果頂級文件嵌入了一個源為 analytics.example.com<iframe> 併發出 fetchLater() 請求,該請求將受制於 128KiB 的限制。

按報告源和子幀劃分的配額限制

對於同一個報告源(請求 URL 的源),頂級 512KiB 配額中最多隻能併發使用 64KiB。這可以防止第三方庫在有資料要傳送之前就機會性地佔用配額。

預設情況下,每個跨源子幀從共享的 128KiB 配額中獲得 8KiB 的配額,該配額在子幀新增到 DOM 時分配(無論該子幀是否會使用 fetchLater())。這意味著,總的來說,頁面上只能使用前 16 個新增的跨源子幀,因為它們會用完 128KiB 的配額。

透過共享頂級配額來增加子幀配額

頂級源可以為選定的跨源子幀分配增加到 64KiB 的配額,這會從更大的頂級 512KiB 限制中扣除。透過在 deferred-fetch 許可權策略指令中列出這些源來實現。這會在子幀新增到 DOM 時分配,從而為頂級文件和直接同源子幀留下更少的配額。多個同源子域可以各自獲得 64KiB 的配額。

限制共享配額

頂級源還可以透過在 deferred-fetch-minimal 許可權策略中列出特定跨源子幀來限制 128KiB 的共享配額。它還可以透過將 deferred-fetch-minimal 許可權策略設定為 () 來撤銷整個 128KiB 的預設子幀配額,轉而將全部 640KiB 配額保留給自己以及任何命名的 deferred-fetch 跨源。

將配額委派給子幀的子幀

預設情況下,子幀的子幀沒有被分配配額,因此無法使用 fetchLater()。被分配了增加到 64KiB 配額的子幀可以透過設定自己的 deferred-fetch 許可權策略,將全部 64KiB 配額委派給更深層的子幀,並允許它們使用 fetchLater()。它們只能將全部配額委派給更深的子幀,而不能委派部分配額,也不能指定新的配額。使用最小 8KiB 配額的子幀無法將配額委派給子幀。要獲得委派的配額,子子幀必須同時包含在頂級和子幀的 deferred-fetch Permissions-Policy 指令中。

超出配額時

當配額被超出時,呼叫 fetchLater() 方法來發起延遲請求會丟擲 QuotaExceededError

許可權策略檢查與配額檢查無法區分。呼叫 fetchLater() 會丟擲 QuotaExceededError,無論是實際超出配額,還是透過許可權策略限制了該源的配額。

fetchLater() 的呼叫者應保持防禦性,幾乎在所有情況下都應捕獲 QuotaExceededError 錯誤,尤其是在嵌入第三方 JavaScript 時。

示例

使用最小配額

http
Permissions-Policy: deferred-fetch=(self "https://b.com")
  1. <iframe src="https://b.com/page"> 在新增到頂級文件時,從頂級文件的 512KiB 限制中獲得 64KiB 配額。
  2. <iframe src="https://c.com/page"> 未被列出,因此從 128KiB 的共享限制中獲得 8KiB 配額。
  3. 另外 15 個跨源 iframe 在新增到頂級文件時,每個都會獲得 8KiB 的配額(類似於 c.com)。
  4. 下一個跨源 iframe 將不會獲得任何配額。
  5. 如果其中一個跨源 iframe 被移除,其延遲的獲取請求將被髮送。
  6. 下一個跨源 iframe 獲得 8KiB 的配額,因為又有可用配額了。

撤銷將最小配額限制為命名源

http
Permissions-Policy: deferred-fetch-minimal=("https://b.com")
  1. <iframe src="https://b.com/page"> 在新增到頂級文件時獲得 8KiB 配額。
  2. <iframe src="https://c.com/page"> 在新增到頂級文件時未獲得任何配額。
  3. 頂級文件及其同源後代最多可以使用 512KiB。

完全撤銷最小配額,但保留頂級例外

http
Permissions-Policy: deferred-fetch=(self "https://b.com")
Permissions-Policy: deferred-fetch-minimal=()
  1. <iframe src="https://b.com/page"> 在新增到頂級文件時獲得 64KiB 配額。
  2. <iframe src="https://c.com/page"> 在新增到頂級文件時未獲得任何配額。
  3. 頂級文件及其同源後代最多可以使用全部 640KiB,但如果建立了一個 b.com 子幀,則會減少到 574KiB(如果建立了多個 b.com 子幀,每個子幀都會分配 64KiB 配額,則會更少)。

完全撤銷最小配額,且沒有任何例外

http
Permissions-Policy: deferred-fetch-minimal=()
  1. 頂級文件及其同源後代可以使用全部 640KiB。
  2. 子幀未被分配任何配額,無法使用 fetchLater()

同源子幀與頂級共享配額,並可以委派給子幀

假設頂級文件位於 a.com,嵌入了一個 a.com 的子幀,該子幀又嵌入了一個 b.com 的子幀,並且沒有顯式的許可權策略。

  1. a.com 的頂級文件擁有預設的 512KiB 配額。
  2. <iframe src="https://a.com/embed"> 在新增到頂級文件時共享 512KiB 配額。
  3. <iframe src="https://b.com/embed"> 在新增到頂級文件時獲得 8KiB 配額。

當被跨源子幀分隔時,同源子幀無法與頂級共享配額

假設頂級文件位於 a.com,嵌入了一個 <iframe src="https://b.com/">,該 iframe 又嵌入了一個 <iframe src="https://a.com/embed">,並且沒有顯式的許可權策略。

  1. a.com 的頂級文件擁有預設的 512KiB 配額。
  2. <iframe src="https://b.com/"> 共享 8KiB 配額。
  3. <iframe src="https://a.com/embed"> 未獲得任何配額;儘管它與頂級源同源,但它被一個跨源分隔開。

子幀的二級子幀預設情況下未獲得配額

假設頂級文件位於 a.com,嵌入了一個 <iframe src="https://b.com/">,該 iframe 又嵌入了一個 <iframe src="https://c.com/">,並且沒有顯式的許可權策略。

  1. a.com 的頂級框架擁有預設的 512KiB 配額。
  2. <iframe src="https://b.com/"> 從預設共享配額中獲得 8KiB。
  3. <iframe src="https://c.com/"> 未獲得任何配額。

授予更深層子幀全部配額

假設頂級文件位於 a.com,嵌入了一個 <iframe src="https://b.com/">,該 iframe 又嵌入了一個 <iframe src="https://c.com/">

假設 a.com 具有以下許可權策略

http
Permissions-Policy: deferred-fetch=("https://c.com" "https://c.com")

並且,假設 b.com 具有以下許可權策略

http
Permissions-Policy: deferred-fetch=("https://c.com")
  1. a.com 的頂級框架擁有預設的 512KiB 配額。
  2. <iframe src="https://b.com/"> 從預設配額中獲得 64KiB。
  3. <iframe src="https://b.com/"> 將其全部 8KiB 配額委派給 c.comb.com 無法使用 fetchLater()
  4. <iframe src="https://c.com/"> 獲得 8KiB 配額。

重定向不會轉移配額

假設頂級文件位於 a.com,嵌入了一個 <iframe src="https://b.com/">,該 iframe 重定向到 c.com,並且沒有顯式的頂級許可權策略。

  1. a.com 的頂級框架擁有預設的 512KiB 配額。
  2. <iframe src="https://b.com/"> 從預設共享配額中獲得 8KiB。
  3. <iframe src="https://b.com/"> 重定向到 c.com 時,8KiB 不會轉移到 c.com,但 8KiB 也不會被釋放。

沙盒化同源 iframe 有效地被視為不同的源

例如,如果在 https://www.example.com 上嵌入了以下 <iframe>

html
<iframe src="https://www.example.com/iframe" sandbox="allow-scripts"></iframe>

儘管此 iframe 與頂級文件託管在同一源上,但由於它位於沙盒環境中,因此不會被視為“同源”。因此,預設情況下,它應該從總共 128KiB 的共享配額中獲得 8KiB 配額。

禁止 iframe 使用 fetchLater()

您可以使用 <iframe>allow 屬性來阻止為 <iframe> 分配 fetchLater() 配額。

html
<iframe
  src="https://www.example.com/iframe"
  allow="deferred-fetch;deferred-fetch-minimal;"></iframe>

需要 allow="deferred-fetch" 指令來防止同源 iframe 耗盡 512KiB 配額,需要 allow="deferred-fetch-minimal" 指令來防止跨源 iframe 耗盡 128KiB 配額。包含這兩個指令將阻止兩個配額的使用,無論 src 值如何。

導致 QuotaExceededError 的示例

js
// Maximum of 64KiB per origin
fetchLater(a_72_kb_url);

// Maximum of 64KiB per origin including headers
fetchLater("https://origin.example.com", { headers: headers_exceeding_64kb });

// Maximum of 64KiB per origin including body and headers
fetchLater(a_32_kib_url, { headers: headers_exceeding_32kib });

// Maximum of 64KiB per origin including body
fetchLater("https://origin.example.com", {
  method: "POST",
  body: body_exceeding_64_kib,
});

// Maximum of 64KiB per origin including body and automatically added headers
fetchLater(a_62_kib_url /* with a 3kb referrer */);

最終導致 QuotaExceededError 的示例

在以下位於頂級文件中的序列中,前兩個請求將成功,但第三個請求會失敗。這是因為,即使總共 640KiB 的配額未被超出,第三個請求也超出了 https://a.example.com 的報告源配額,並將導致錯誤。

js
fetchLater("https://a.example.com", { method: "POST", body: a_40kb_body });
fetchLater("https://b.example.com", { method: "POST", body: a_40kb_body });
fetchLater("https://a.example.com", { method: "POST", body: a_40kb_body });

將子幀重定向回頂級源允許使用頂級配額

假設頂級文件位於 a.com,嵌入了 <iframe src="https://b.com/">,該 iframe 重定向到 a.com,並且沒有顯式的頂級許可權策略。

  1. a.com 的頂級框架擁有預設的 512KiB 配額。
  2. <iframe src="https://b.com/"> 從 128KiB 的預設共享配額中獲得 8KiB。
  3. <iframe src="https://b.com/"> 重定向到 a.com 時,8KiB 不會轉移到 a.com,但它可以再次共享全部頂級配額,並且之前分配的 8KiB 配額將被釋放。

規範

規範
Fetch
# available-deferred-fetch-quota

瀏覽器相容性

http.headers.Permissions-Policy.deferred-fetch

http.headers.Permissions-Policy.deferred-fetch-minimal

另見