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 是網際網路上第一個用於用戶端儲存的機制,我們也從中學到很多。

避免使用 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 的熱門應用程式會偵測使用者登出時機,並更新使用者介面。目前是透過輪詢 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 變更的方式稍有不同。喚醒服務工作者可能會耗費大量資源,因此我們必須明確說明 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 元件。

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

其他資源