HTTP Cookie 非同步存取

Victor Costan

什麼是 Cookie Store API?

Cookie Store API 會將 HTTP Cookie 公開給服務工作站,並提供 document.cookie 的非同步替代方案。這個 API 可簡化以下作業:

  • 以非同步方式存取 Cookie,避免主執行緒卡頓。
  • 系統會觀測 Cookie 的變更,因此請避免輪詢 Cookie。
  • 透過服務工作站存取 Cookie。

閱讀說明

目前狀態

步驟 狀態
1. 建立說明 完成
2. 建立規格初稿 完成
**3. 收集意見回饋並重複執行規格** **進行中**
4. 來源試用 已暫停
5. 啟動 尚未開始

如何使用非同步 Cookie 儲存庫?

啟用來源試用方案

如要本機測試,您可以在指令列上啟用 API:

chrome --enable-blink-features=CookieStore

在指令列中傳遞此標記,即可在 Chrome 中為目前的工作階段啟用全域 API 功能。

或者,您也可以在 chrome://flags 中啟用 #enable-experimental-web-platform-features 標記。

(可能) 不需要 Cookie

在深入探討新 API 之前,我想先說明一下,Cookie 仍是網路平台上最糟的用戶端儲存原始碼,因此應視為最後手段。這不是意外,Cookie 是 Web 的第一個用戶端儲存機制,我們從那時就學到了很多。

避免使用 Cookie 的主要原因包括:

  • Cookie 會將儲存空間結構定義帶入後端 API。每個 HTTP 要求都會附帶 Cookie Jar 的快照。這樣一來,後端工程師就能輕鬆在目前的 Cookie 格式中引入依附元件。發生這種情況時,前端無法變更儲存格式,除非後端也部署相符的變更。

  • Cookie 的安全性模型相當複雜。新型網路平台功能遵循相同的來源政策,也就是說,每個應用程式都有專屬的沙箱,而且與使用者可能執行的其他應用程式完全無關。Cookie 範圍不僅可產生更加複雜的安全性故事,而且只是嘗試總結,這項功能會增加本文的兩倍。

  • Cookie 的效能成本很高。瀏覽器需要在每個 HTTP 要求中加入 Cookie 快照,因此 Cookie 的每項變更都必須在儲存空間和網路堆疊中傳播。現代瀏覽器已實作經過高度最佳化的 Cookie 儲存機制,但我們永遠無法讓 Cookie 的效率與其他儲存機制 (不需要與網路堆疊通訊) 一樣高。

基於上述所有原因,新型網頁應用程式應避免使用 Cookie,而是將工作階段 ID 儲存至 IndexedDB,並透過 fetch API 將 ID 明確新增至特定 HTTP 要求的標頭或內文。

不過,您仍在閱讀本文,表示您有使用 Cookie 的充分理由...

可長期使用的 document.cookie API 是對應用程式保證的卡頓來源。舉例來說,當您使用 document.cookie getter 時,瀏覽器必須停止執行 JavaScript,直到其取得您要求的 Cookie 資訊為止。這可能會進行程序躍點或磁碟讀取作業,並導致 UI 發生卡頓。

解決這個問題的簡單方法,就是從 document.cookie getter 切換至非同步 Cookie Store API。

await cookieStore.get('session_id');

// {
//   domain: "example.com",
//   expires: 1593745721000,
//   name: "session_id",
//   path: "/",
//   sameSite: "unrestricted",
//   secure: true,
//   value: "yxlgco2xtqb.ly25tv3tkb8"
// }

document.cookie setter 可透過類似方式替換。請注意,變更只會在 cookieStore.set 傳回的 Promise 解析後才會套用。

await cookieStore.set({name: 'opt_out', value: '1'});

// undefined

觀察,不要意見調查

當一個熱門應用程式透過 JavaScript 存取 Cookie 時,就會偵測使用者登出並更新 UI 的情形。目前是透過輪詢 document.cookie 來執行這項操作,但這會導致卡頓現象,並對電池續航力造成負面影響。

Cookie Store API 提供另一種觀察 Cookie 變更的方法,不需要輪詢。

cookieStore.addEventListener('change', event => {
  for (const cookie of event.changed) {
    if (cookie.name === 'session_id') sessionCookieChanged(cookie.value);
  }
  for (const cookie of event.deleted) {
    if (cookie.name === 'session_id') sessionCookieChanged(null);
  }
});

歡迎使用 Service Worker

基於同步設計,document.cookie API 尚未提供給服務工作站使用。Cookie Store API 是非同步的,因此可在服務 worker 中使用。

在文件內容和服務工作程式中,與 Cookie 互動的運作方式相同。

// Works in documents and service workers.
async function logOut() {
  await cookieStore.delete('session_id');
}

不過,在服務工作程式中觀察 Cookie 變更的方式稍有不同。喚醒 Service Worker 所費不貲,因此我們必須明確說明工作站所需的 Cookie 變更。

在以下範例中,使用 IndexedDB 快取使用者資料的應用程式,會監控工作階段 Cookie 的變更,並在使用者登出時捨棄快取資料。

// Specify the cookie changes we're interested in during the install event.
self.addEventListener('install', event => {
  event.waitUntil(cookieStore.subscribeToChanges([{name: 'session_id'}]));
});

// Delete cached data when the user logs out.
self.addEventListener('cookiechange', event => {
  for (const cookie of event.deleted) {
    if (cookie.name === 'session_id') {
      indexedDB.deleteDatabase('user_cache');
      break;
    }
  }
});

最佳做法

即將推出。

意見回饋

歡迎試用這個 API,並與我們分享您的想法!請將 API 形狀的意見回饋直接傳送至規格存放區,並將實作錯誤回報至 Blink>Storage>CookiesAPI Blink 元件。

我們特別想瞭解成效評估和用途,而非說明文件中所述的用途。

其他資源