HTTP Cookie 非同步存取

Victor Costan

什麼是 Cookie Store API?

Cookie Store API 會向服務 Worker 公開 HTTP Cookie,並提供 document.cookie 的非同步替代選項。這個 API 可讓您輕鬆:

  • 以非同步方式存取 Cookie,避免主執行緒發生卡頓。
  • 請避免輪詢 Cookie,因為系統會觀察 Cookie 的變化。
  • 從 Service Worker 存取 Cookie。

閱讀說明

目前狀態

步驟 狀態
1. 建立說明 完成
2. 建立規格的初始草稿 完成
**3. 收集意見回饋並持續改進規格** **進行中**
4. 來源試用 已暫停
5. 啟動 Not started

如何使用非同步 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 在特定 HTTP 要求的標頭或內文中明確加入 ID。

話雖如此,我們仍在閱讀這篇文章,是因為您對 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 為非同步性質,因此可在服務工作站中使用。

在文件結構定義和服務工作程式中,與 Cookie 互動的方式相同。

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

不過,在服務工作處理程序中觀察 Cookie 變更的部分,略有不同。喚醒服務工作站的費用可能相當高昂,因此我們必須明確說明工作站感興趣的 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 閃爍元件回報實作錯誤。

我們尤其希望瞭解說明中未提及的效能評估功能和用途。

其他資源