local 保存裝置上的資料,sync 在已登入的設定檔之間攜帶少量偏好,session 只要瀏覽器在執行就留在記憶體裡,managed 則是唯讀的政策資料。本頁給出每個區域的配額與可讀方,說明讓 storage.session 遠離 content script 的規則,以及區域寫滿或被限流時印出的主控台行。
保存狀態的三種方式,以及為什麼 storage 勝出
service worker 裡的變數。 Manifest V3 的背景 service worker 會在閒置時停止,並在下一個事件到來時重新啟動。你放在記憶體裡的一切都消失了,而且沒有任何錯誤來提醒你。 擴充功能頁面裡的localStorage 或 IndexedDB。 兩者都以頁面來源為範圍。service worker 根本不能使用 localStorage,而 content script 看到的是宿主頁面的儲存,不是你的。
一個 chrome.storage 區域。 非同步,任何擁有 storage 權限的擴充功能情境都能讀取,也不受 worker 重新啟動影響。這是擴充功能狀態的預設答案。
四個儲存區域
下面的配額是 Chromium 的預設值。sync 還會限流寫入:大約每分鐘 120 次、每小時 1,800 次。放在按鍵處理常式裡的寫入會很快撞到這個上限,所以請做去抖動,只寫一次。
依資料回答的問題來選擇:
- 功能狀態、快取與持久設定放在
local。 - 使用者期望在第二台電腦上看到的少量偏好放在
sync。 - 不該活過瀏覽器的單次執行協調資料放在
session。 - 企業政策值從
managed讀取,絕不與使用者可編輯的設定混在一起。
chrome.storage 不是唯一的選擇。IndexedDB 和來源私有檔案系統(OPFS)可以在擴充功能頁面和 worker 中存放大量或結構化資料,受瀏覽器的一般配額約束。在 content script 中,兩者都屬於宿主頁面的來源,因此請把這類資料放在擴充功能自己的情境裡。
Manifest 片段
storage 權限涵蓋全部四個區域。unlimitedStorage 提高 local 的上限,managed 需要一個 schema 檔案:
從 content script 讀取 storage.session
storage.session 一開始對不受信任的情境是關閉的,而 content script 正是這種情境。從那裡讀取會擲出錯誤,而不是回傳一個空物件。請在 content script 發出請求之前,從 service worker 裡開放一次這個區域:
各瀏覽器差異
Firefox 實作了全部四個區域。請把配額表裡的數字當作 Chromium 的,並在發布前對著 Firefox 建置核對你的功能所依賴的那個上限。
如果你的原始碼呼叫
browser.storage,同時也要建置 Chromium 目標,請傳入 --polyfill。請見 跨瀏覽器相容。
你會看到的主控台錯誤
把你看到的那一行複製去搜尋。每一行對應一個原因。QUOTA_BYTES_PER_ITEM quota exceeded
一個 sync 鍵之下的單一值大於 8 KB。把這個值拆到多個鍵,或者把該鍵移到 local。
QUOTA_BYTES quota exceeded
區域滿了:sync 是所有鍵合計 100 KB,local 是裝置上限。存少一點,或者為 local 這種情況加上 unlimitedStorage。
MAX_WRITE_OPERATIONS_PER_MINUTE quota exceeded
一分鐘內的 sync 寫入過多,通常是每次按鍵或每次捲動都寫一次。給處理常式做去抖動,只寫一次穩定後的值。
Access to storage is not allowed from this context.
content script 在區域仍然僅限受信任情境時讀取了 storage.session。請從 service worker 呼叫 setAccessLevel,或者把讀取放到一則訊息之後。
回呼風格的呼叫透過 chrome.runtime.lastError 回報這些錯誤,而在你去讀它之前它是靜默的。Promise 形式會 reject,因此在 try 區塊裡 await,你會看到同樣的文字作為被捕捉的錯誤。
Extension.js 的做法
Extension.js 不包裝也不取代儲存 API。它只編譯呼叫這些 API 的程式碼,因此上面的平台規則就是全部約定。有兩個建置行為值得知道:storage.managed_schema路徑會被驗證,schema 檔案會被產出到輸出目錄。該路徑必須解析到擴充功能目錄內的檔案。Extension.js 會在瀏覽器啟動前把指向目錄之外的路徑標記為載入阻斷,因為 Chrome 會因為缺少 managed schema 而拒絕整個擴充功能。請見 JSON。--polyfill會把browser.*橋接到 Chromium 目標上,因此同一份原始碼可以為兩個家族呼叫browser.storage。

