Javascript LocalStorage,sessionStorage

2023-02-17 10:57 更新

Web 存儲對象 ?localStorage? 和 ?sessionStorage? 允許我們在瀏覽器上保存鍵/值對。

它們有趣的是,在頁面刷新后(對于 sessionStorage)甚至瀏覽器完全重啟(對于 localStorage)后,數(shù)據(jù)仍然保留在瀏覽器中。我們很快就會看到。

我們已經有了 cookie。為什么還要其他存儲對象呢?

  • 與 cookie 不同,Web 存儲對象不會隨每個請求被發(fā)送到服務器。因此,我們可以保存更多數(shù)據(jù)。大多數(shù)現(xiàn)代瀏覽器都允許保存至少 5MB 的數(shù)據(jù)(或更多),并且具有用于配置數(shù)據(jù)的設置。
  • 還有一點和 cookie 不同,服務器無法通過 HTTP header 操縱存儲對象。一切都是在 JavaScript 中完成的。
  • 存儲綁定到源(域/協(xié)議/端口三者)。也就是說,不同協(xié)議或子域對應不同的存儲對象,它們之間無法訪問彼此數(shù)據(jù)。

兩個存儲對象都提供相同的方法和屬性:

  • ?setItem(key, value)? —— 存儲鍵/值對。
  • ?getItem(key)? —— 按照鍵獲取值。
  • ?removeItem(key)? —— 刪除鍵及其對應的值。
  • ?clear()? —— 刪除所有數(shù)據(jù)。
  • ?key(index)? —— 獲取該索引下的鍵名。
  • ?length? —— 存儲的內容的長度。

正如你所看到的,它就像一個 Map 集合(setItem/getItem/removeItem),但也允許通過 key(index) 來按索引訪問。

讓我們看看它是如何工作的吧。

localStorage 示例

localStorage 最主要的特點是:

  • 在同源的所有標簽頁和窗口之間共享數(shù)據(jù)。
  • 數(shù)據(jù)不會過期。它在瀏覽器重啟甚至系統(tǒng)重啟后仍然存在。

例如,如果你運行此代碼……

localStorage.setItem('test', 1);

……然后關閉/重新打開瀏覽器,或者只是在不同的窗口打開同一頁面,然后你可以這樣獲取它:

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

我們只需要在同一個源(域/端口/協(xié)議),URL 路徑可以不同。

在所有同源的窗口之間,localStorage 數(shù)據(jù)可以共享。因此,如果我們在一個窗口中設置了數(shù)據(jù),則在另一個窗口中也可以看到數(shù)據(jù)變化。

類對象形式訪問

我們還可以像使用一個普通對象那樣,讀取/設置鍵,像這樣:

// 設置 key
localStorage.test = 2;

// 獲取 key
alert( localStorage.test ); // 2

// 刪除 key
delete localStorage.test;

這是歷史原因造成的,并且大多數(shù)情況下都可行,但通常不建議這樣做,因為:

  1. 如果鍵是由用戶生成的,那么它可以是任何內容,例如 length 或 toString,也可以是 localStorage 的另一種內建方法。在這種情況下,getItem/setItem 可以正常工作,而類對象訪問的方式則會失?。?/li>
    let key = 'length';
    localStorage[key] = 5; // Error,無法對 length 進行賦值
  2. 有一個 ?storage? 事件,在我們更改數(shù)據(jù)時會觸發(fā)。但以類對象方式訪問時,不會觸發(fā)該事件。我們將在本章的后面看到。

遍歷鍵

正如我們所看到的,這些方法提供了“按照鍵獲取/設置/刪除”的功能。但我們如何獲取所有保存的值或鍵呢?

不幸的是,存儲對象是不可迭代的。

一種方法是像遍歷數(shù)組那樣遍歷它們:

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

另一個方式是使用 for key in localStorage 循環(huán),就像處理常規(guī)對象一樣。

它會遍歷所有的鍵,但也會輸出一些我們不需要的內建字段。

// 不好的嘗試
for(let key in localStorage) {
  alert(key); // 顯示 getItem,setItem 和其他內建的東西
}

……因此,我們需要使用 hasOwnProperty 檢查來過濾掉原型中的字段:

for(let key in localStorage) {
  if (!localStorage.hasOwnProperty(key)) {
    continue; // 跳過像 "setItem","getItem" 等這樣的鍵
  }
  alert(`${key}: ${localStorage.getItem(key)}`);
}

……或者,使用 Object.keys 獲取只屬于“自己”的鍵,然后如果需要,可以遍歷它們:

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

后者有效,因為 Object.keys 只返回屬于對象的鍵,會忽略原型上的。

僅字符串

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

如果是任何其他類型,例數(shù)字或對象,它會被自動轉換為字符串。

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

也可以對整個存儲對象進行字符串化處理,例如出于調試目的:

// 為 JSON.stringify 增加了格式設置選項,以使對象看起來更美觀
alert( JSON.stringify(localStorage, null, 2) );

sessionStorage

sessionStorage 對象的使用頻率比 localStorage 對象低得多。

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

  • ?sessionStorage? 的數(shù)據(jù)只存在于當前瀏覽器標簽頁。
    • 具有相同頁面的另一個標簽頁中將會有不同的存儲。
    • 但是,它在同一標簽頁下的 iframe 之間是共享的(假如它們來自相同的源)。
  • 數(shù)據(jù)在頁面刷新后仍然保留,但在關閉/重新打開瀏覽器標簽頁后不會被保留。

讓我們看看它的運行效果。

運行此代碼……

sessionStorage.setItem('test', 1);

……然后刷新頁面。這時你仍然可以獲取到數(shù)據(jù):

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

……但是,如果你在另一個新的標簽頁中打開此頁面,然后在新頁面中再次運行上面這行代碼,則會得到 null,表示“未找到數(shù)據(jù)”。

這是因為 sessionStorage 不僅綁定到源,還綁定在同一瀏覽器標簽頁。因此,sessionStorage 很少被使用。

Storage 事件

當 localStorage 或 sessionStorage 中的數(shù)據(jù)更新后,storage 事件就會觸發(fā),它具有以下屬性:

  • ?key? —— 發(fā)生更改的數(shù)據(jù)的 ?key?(如果調用的是 ?.clear()? 方法,則為 ?null?)。
  • ?oldValue? —— 舊值(如果是新增數(shù)據(jù),則為 ?null?)。
  • ?newValue? —— 新值(如果是刪除數(shù)據(jù),則為 ?null?)。
  • ?url? —— 發(fā)生數(shù)據(jù)更新的文檔的 url。
  • ?storageArea? —— 發(fā)生數(shù)據(jù)更新的 ?localStorage? 或 ?sessionStorage? 對象。

重要的是:該事件會在所有可訪問到存儲對象的 window 對象上觸發(fā),導致當前數(shù)據(jù)改變的 window 對象除外。

我們來詳細解釋一下。

想象一下,你有兩個窗口,它們具有相同的頁面。所以 localStorage 在它們之間是共享的。

你可以想在瀏覽器的兩個窗口中打開此頁面來測試下面的代碼。

如果兩個窗口都在監(jiān)聽 window.onstorage 事件,那么每個窗口都會對另一個窗口中發(fā)生的更新作出反應。

// 在其他文檔對同一存儲進行更新時觸發(fā)
window.onstorage = event => { // 也可以使用 window.addEventListener('storage', event => {
  if (event.key != 'now') return;
  alert(event.key + ':' + event.newValue + " at " + event.url);
};

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

請注意,該事件還包含:event.url —— 發(fā)生數(shù)據(jù)更新的文檔的 url。

并且,event.storageArea 包含存儲對象 —— sessionStorage 和 localStorage 具有相同的事件,所以 event.storageArea 引用了被修改的對象。我們可能會想設置一些東西,以“響應”更改。

這允許同源的不同窗口交換消息。

現(xiàn)代瀏覽器還支持 Broadcast channel API,這是用于同源窗口之間通信的特殊 API,它的功能更全,但被支持的情況不好。有一些庫基于 localStorage 來 polyfill 該 API,使其可以用在任何地方。

總結

Web 存儲對象 ?localStorage? 和 ?sessionStorage? 允許我們在瀏覽器中保存鍵/值對。

  • ?key? 和 ?value? 都必須為字符串。
  • 存儲大小限制為 5MB+,具體取決于瀏覽器。
  • 它們不會過期。
  • 數(shù)據(jù)綁定到源(域/端口/協(xié)議)。
localStorage sessionStorage
在同源的所有標簽頁和窗口之間共享數(shù)據(jù) 在當前瀏覽器標簽頁中可見,包括同源的 iframe
瀏覽器重啟后數(shù)據(jù)仍然保留 頁面刷新后數(shù)據(jù)仍然保留(但標簽頁關閉后數(shù)據(jù)則不再保留)

API:

  • ?setItem(key, value)? —— 存儲鍵/值對。
  • ?getItem(key)? —— 按照鍵獲取值。
  • ?removeItem(key)? —— 刪除鍵及其對應的值。
  • ?clear()? —— 刪除所有數(shù)據(jù)。
  • ?key(index)? —— 獲取該索引下的鍵名。
  • ?length? —— 存儲的內容的長度。
  • 使用 ?Object.keys? 來獲取所有的鍵。
  • 我們將鍵作為對象屬性來訪問,在這種情況下,不會觸發(fā) ?storage? 事件。

Storage 事件:

  • 在調用 ?setItem?,?removeItem?,?clear? 方法后觸發(fā)。
  • 包含有關操作的所有數(shù)據(jù)(?key/oldValue/newValue?),文檔 ?url? 和存儲對象 ?storageArea?。
  • 在所有可訪問到存儲對象的 ?window? 對象上觸發(fā),導致當前數(shù)據(jù)改變的 ?window? 對象除外(對于 ?sessionStorage? 是在當前標簽頁下,對于 ?localStorage? 是在全局,即所有同源的窗口)。

任務


自動保存表單字段

創(chuàng)建一個 textarea 字段,每當其值發(fā)生變化時,可以將其“自動保存”。

因此,如果用戶不小心關閉了頁面,然后重新打開,他會發(fā)現(xiàn)之前未完成的輸入仍然保留在那里。

打開一個任務沙箱。


解決方案

使用沙箱打開解決方案。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號