在進行Chrome外掛開發時,經常需要將資料儲存在瀏覽器本地。這看起來是一個簡單直接的任務,但是這個簡單的操作出大事了。
問題描述
最近俺在接觸 Chrome 外掛開發,需要把一個數據存放到瀏覽器的儲存中。這個數據結構有點複雜,它包含一個 Map 和一個數組。我使用 chrome.storage.local
API來儲存這個資料,然後在另一個地方獲取資料。儲存資料的程式碼並沒有報錯,但是俺發現獲取的時候獲取結果的內容為空,這是為什麼呢?
下面是我封裝的儲存資料和獲取資料的方法:
// 要儲存的數據結構 const mindDataObj = { keywordMap: new Map(), mindDataArr: [] }; /** * 獲取儲存物件 * @param {string} key 儲存物件的鍵 * @returns {Promise<Object>} 返回一個包含儲存物件的 Promise */ export async function getObject(key) { return new Promise((resolve, reject) => { chrome.storage.local.get([key], (result) => { if (chrome.runtime.lastError) { return reject(chrome.runtime.lastError) } resolve(result[key] || {}) }) }) } /** * 儲存儲存物件 * @param {string} key 儲存物件的鍵 * @param {Object} obj 要儲存的物件 * @returns {Promise<void>} 返回一個 Promise,表示操作完成 */ export async function saveObject(key, obj) { return new Promise((resolve, reject) => { chrome.storage.local.set({ [key]: obj }, () => { if (chrome.runtime.lastError) { return reject(chrome.runtime.lastError) } resolve() }) }) }
為什麼會這樣?
經過查詢資料發現,chrome.storage.local
的儲存機制只能儲存和檢索序列化的 JSON 物件,雖然JSON可以很好地處理物件和陣列,但對於Map
、Set
等ES6中引入的複雜數據結構,JSON是無法直接序列化和反序列化的。因此,儘管你可能沒有在儲存資料時遇到錯誤,但在嘗試讀取非JSON相容型別的資料時,這些資料將因無法被正確序列化而丟失。
解決方案
總之一句話:chrome.storage.local
只能儲存 JSON 相容的資料型別(如物件、陣列、字串、數字等),Map
和 Set
需要轉換為物件或陣列才能正確儲存。在這裏,我們透過 chrome.storage.local
儲存時需要先進行序列化處理,而在讀取時需要進行反序列化處理。
步驟1: 序列化和反序列化Map物件
我們先增加兩個方法做序列化的處理,serializeMap
和 deserializeMap
方法用於將 Map
物件轉換為陣列,從而可以儲存在 chrome.storage.local
中,並在讀取時將其轉換回 Map
物件。
/** * 序列化Map物件 * @param {Map} map 要序列化的Map物件 * @returns {Object} 序列化後的物件 */ function serializeMap(map) { return Array.from(map.entries()); } /** * 反序列化Map物件 * @param {Array} entries 序列化後的物件 * @returns {Map} 反序列化後的Map物件 */ function deserializeMap(entries) { return new Map(entries); }
步驟2: 儲存和讀取資料
然後再增加運算元據的方法,saveMindData
和 getMindData
方法用於儲存和獲取 mindDataObj
格式的資料,包括序列化和反序列化步驟。
/** * 儲存mindDataObj格式的資料 * @param {string} key 儲存物件的鍵 * @param {Object} initMindDataObj 要儲存的物件 * @returns {Promise<void>} 返回一個 Promise,表示操作完成 */ export async function saveMindData(key, mindDataObj) { const serializedData = { keywordMap: serializeMap(mindDataObj.keywordMap), mindDataArr: mindDataObj.mindDataArr }; await saveObject(key, serializedData); } /** * 獲取mindDataObj格式的資料 * @param {string} key 儲存物件的鍵 * @returns {Promise<Object>} 返回一個包含mindDataObj格式資料的 Promise */ export async function getMindData(key) { const serializedData = await getObject(key); const mindDataObj = { keywordMap: deserializeMap(serializedData.keywordMap || []), mindDataArr: serializedData.mindDataArr || [] }; return mindDataObj; }