2022 年 10 月 5 日

LocalStorage、sessionStorage

網頁儲存物件 localStoragesessionStorage 允許在瀏覽器中儲存金鑰/值對。

它們有趣的地方在於,這些資料可以在頁面重新整理(對於 sessionStorage)甚至瀏覽器完全重新啟動(對於 localStorage)後仍然存在。我們很快就會看到。

我們已經有 Cookie 了。為什麼需要額外的物件?

  • 與 Cookie 不同,網頁儲存物件不會在每次要求時傳送至伺服器。因此,我們可以儲存更多資料。大多數現代瀏覽器允許至少 5 MB 的資料(或更多),並有設定可以設定這些資料。
  • 與 Cookie 不同的是,伺服器無法透過 HTTP 標頭來操作儲存物件。所有操作都必須透過 JavaScript 執行。
  • 儲存空間與來源 (網域/通訊協定/埠號三元組) 相關。也就是說,不同的通訊協定或次網域會產生不同的儲存物件,它們無法彼此存取資料。

兩種儲存物件都提供相同的函式和屬性

  • setItem(key, value) – 儲存 key/value 成對資料。
  • getItem(key) – 透過 key 取得 value。
  • removeItem(key) – 移除 key 及其 value。
  • clear() – 刪除所有資料。
  • key(index) – 取得指定位置的 key。
  • length – 已儲存項目數。

如你所見,它就像一個 Map 集合 (setItem/getItem/removeItem),但也可以透過 key(index) 根據索引來存取。

讓我們看看它是如何運作的。

localStorage 示範

localStorage 的主要功能為

  • 在來自相同來源的所有分頁和視窗之間共用。
  • 資料不會過期。它會在瀏覽器重新啟動,甚至作業系統重新開機後仍然存在。

例如,如果你執行這段程式碼…

localStorage.setItem('test', 1);

…然後關閉/開啟瀏覽器,或是在不同的視窗中開啟同一頁面,你就可以像這樣取得資料

alert( localStorage.getItem('test') ); // 1

我們只需要在相同的來源 (網域/埠號/通訊協定) 中,網址路徑可以不同。

localStorage 在所有具有相同來源的視窗之間共用,因此如果我們在一個視窗中設定資料,變更會在另一個視窗中顯示出來。

類似物件的存取

我們也可以使用一般物件的方式來取得/設定 key,如下所示

// set key
localStorage.test = 2;

// get key
alert( localStorage.test ); // 2

// remove key
delete localStorage.test;

由於歷史原因,這種方式是被允許的,而且大多數情況下都能正常運作,但通常不建議使用,因為

  1. 如果 key 是使用者產生的,它可以是任何東西,例如 lengthtoString,或是 localStorage 的另一個內建函式。在這種情況下,getItem/setItem 可以正常運作,但類似物件的存取會失敗

    let key = 'length';
    localStorage[key] = 5; // Error, can't assign length
  2. 有一個 storage 事件,它會在我們修改資料時觸發。這個事件不會發生在類似物件的存取中。我們將在本章稍後看到這一點。

迴圈遍歷 key

如我們所見,這些函式提供「透過 key 取得/設定/移除」功能。但是如何取得所有已儲存的值或 key?

不幸的是,儲存物件不可迭代。

一種方法是像陣列一樣迴圈遍歷它們

for(let i=0; i<localStorage.length; i++) {
  let key = localStorage.key(i);
  alert(`${key}: ${localStorage.getItem(key)}`);
}

另一種方式是使用 for key in localStorage 迴圈,就像我們使用一般物件一樣。

它會反覆運算鍵,但也會輸出我們不需要的幾個內建欄位

// bad try
for(let key in localStorage) {
  alert(key); // shows getItem, setItem and other built-in stuff
}

…因此,我們需要使用 hasOwnProperty 檢查來過濾原型中的欄位

for(let key in localStorage) {
  if (!localStorage.hasOwnProperty(key)) {
    continue; // skip keys like "setItem", "getItem" etc
  }
  alert(`${key}: ${localStorage.getItem(key)}`);
}

…或僅取得「自己的」鍵,使用 Object.keys,然後在需要時反覆運算它們

let keys = Object.keys(localStorage);
for(let key of keys) {
  alert(`${key}: ${localStorage.getItem(key)}`);
}

後者會運作,因為 Object.keys 僅傳回屬於物件的鍵,忽略原型。

僅限字串

請注意,鍵和值都必須是字串。

如果它們是任何其他類型,例如數字或物件,它們會自動轉換為字串

localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]

不過,我們可以使用 JSON 來儲存物件

localStorage.user = JSON.stringify({name: "John"});

// sometime later
let user = JSON.parse( localStorage.user );
alert( user.name ); // John

此外,也可以將整個儲存物件字串化,例如用於除錯目的

// added formatting options to JSON.stringify to make the object look nicer
alert( JSON.stringify(localStorage, null, 2) );

sessionStorage

sessionStorage 物件的使用頻率遠低於 localStorage

屬性和方法相同,但它的限制更多

  • sessionStorage 僅存在於目前的瀏覽器分頁中。
    • 具有相同頁面的另一個分頁會有不同的儲存空間。
    • 但它會在同一個分頁中的 iframe 之間共用(假設它們來自相同的來源)。
  • 資料會保留在頁面重新整理後,但不會保留在關閉/開啟分頁後。

讓我們實際看看。

執行這段程式碼…

sessionStorage.setItem('test', 1);

…然後重新整理頁面。現在你仍然可以取得資料

alert( sessionStorage.getItem('test') ); // after refresh: 1

…但如果你在另一個分頁中開啟相同的頁面,然後在那裡再試一次,上面的程式碼會傳回 null,表示「找不到」。

這正是因為 sessionStorage 不僅與來源繫結,也與瀏覽器分頁繫結。由於這個原因,sessionStorage 的使用很有限。

儲存事件

localStoragesessionStorage 中的資料更新時,儲存 事件會觸發,並具有以下屬性

  • key – 已變更的鍵(如果呼叫 .clear(),則為 null)。
  • oldValue – 舊值(如果鍵是新加入的,則為 null)。
  • newValue – 新值(如果鍵已移除,則為 null)。
  • url – 發生更新的文件網址。
  • storageArea – 發生更新的 localStoragesessionStorage 物件。

重點是:事件觸發在所有可以存取儲存空間的 window 物件上,除了造成事件的那個物件。

讓我們詳細說明。

想像一下,你在每個視窗中開啟同一個網站。因此 localStorage 在它們之間共用。

你可能想要在兩個瀏覽器視窗中開啟這個頁面,來測試下面的程式碼。

如果兩個視窗都在監聽 window.onstorage,那麼每個視窗都會對另一個視窗中發生的更新做出反應。

// triggers on updates made to the same storage from other documents
window.onstorage = event => { // can also use window.addEventListener('storage', event => {
  if (event.key != 'now') return;
  alert(event.key + ':' + event.newValue + " at " + event.url);
};

localStorage.setItem('now', Date.now());

請注意,事件還包含:event.url - 更新資料的文件網址。

此外,event.storageArea 包含儲存空間物件 - 這個事件對 sessionStoragelocalStorage 都是相同的,因此 event.storageArea 參照被修改的那個。我們甚至可能想要在其中設定一些東西,來「回應」變更。

這允許來自同一個來源的不同視窗交換訊息。

現代瀏覽器也支援 廣播頻道 API,這是用於同來源視窗間通訊的特殊 API,它的功能更齊全,但支援度較低。有一些函式庫可以根據 localStorage 來填補這個 API,讓它可以在任何地方使用。

摘要

網頁儲存空間物件 localStoragesessionStorage 允許在瀏覽器中儲存金鑰/值對。

  • keyvalue 都必須是字串。
  • 限制為 5mb+,視瀏覽器而定。
  • 它們不會過期。
  • 資料與來源(網域/埠/通訊協定)綁定。
localStorage sessionStorage
在所有具有相同來源的分頁和視窗之間共用 在瀏覽器分頁中可見,包括來自相同來源的 iframe
瀏覽器重新啟動後仍存在 頁面重新整理後仍存在(但分頁關閉後則不會)

API

  • setItem(key, value) – 儲存 key/value 成對資料。
  • getItem(key) – 透過 key 取得 value。
  • removeItem(key) – 移除 key 及其 value。
  • clear() – 刪除所有資料。
  • key(index) - 取得金鑰編號 index
  • length – 已儲存項目數。
  • 使用 Object.keys 來取得所有金鑰。
  • 我們以物件屬性的方式存取金鑰,在這種情況下,storage 事件不會被觸發。

儲存事件

  • setItemremoveItemclear 呼叫時觸發。
  • 包含有關操作的所有資料(key/oldValue/newValue)、文件 url 和儲存空間物件 storageArea
  • 在所有可以存取儲存空間的 window 物件上觸發,除了產生儲存空間的物件(在 sessionStorage 的分頁中,在 localStorage 的全域中)。

任務

建立一個 textarea 欄位,在每次變更時「自動儲存」其值。

因此,如果使用者意外關閉頁面,然後再次開啟,他會發現未完成的輸入仍保留在原處。

像這樣

為任務開啟沙盒。

教學課程地圖

留言

留言前請先閱讀…
  • 如果您有改善建議 - 請提交 GitHub 議題或提出プルリクエスト,而非留言。
  • 如果您無法理解文章中的某部分 - 請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,若要插入多行,請將其包覆在 <pre> 標籤中,若要插入 10 行以上,請使用沙盒 (plnkrjsbincodepen…)