Stories

Detail Return Return

Web前端入門第 84 問:JavaScript sessionStorage 那些容易踩坑的地方 - Stories Detail

sessionStorage 與 localStorage 差不多可以算作一對兄弟,它倆的暴露的 API 方法一模一樣。

但兩者也有不同點:

1、sessionStorage 存入的數據在頁面關閉後,會自動清除。
2、相同 URL 的每個 tab 頁籤的 sessionStorage 會被隔離,互不影響。也就是説相同的鏈接,在 A 標籤頁打開和在 B 標籤打開,A 寫入的 sessionStorage 數據,B 是無法讀取的!!這裏與 會話 Cookie 完全不一樣。
3、在頁面中使用 <a target="_blank" rel="opener">window.open 打開新的標籤頁,sessionStorage 數據會複製,但之後的修改相互隔離。
4、sessionStorage 存入的數據會在瀏覽器打開期間一直保留,刷新頁面或使用 ctrl + shift + t 恢復頁面時,數據不會丟失。

API

sessionStorage 暴露的接口與 localStorage 一致:

// 只讀屬性,返回會話存儲項的數目
sessionStorage.length

// 獲取會話存儲的第 n 個鍵名
sessionStorage.key(n)
// 獲取會話存儲的值
sessionStorage.getItem(key)
// 寫入會話存儲
sessionStorage.setItem(key, value)
// 刪除會話存儲
sessionStorage.removeItem(key)
// 清空所有會話存儲
sessionStorage.clear()

API 使用示例:

(() => {
  // 寫入數據
  sessionStorage.setItem('type', '公眾號');
  sessionStorage.setItem('name', '前端路引');

  // 獲取會話存儲長度
  console.log(sessionStorage.length);

  // 循環打印會話存儲
  for (let i = 0; i < sessionStorage.length; i++) {
    const key = sessionStorage.key(i);
    const value = sessionStorage.getItem(key);
    console.log(`${key}: ${value}`);
  }
  // 移除單個會話存儲
  sessionStorage.removeItem('name');
  // 移除所有會話存儲
  sessionStorage.clear();
})()

無法監聽存儲數據變化

不像 localStorage,sessionStorage 沒有暴露 onchange 事件,所以無法監聽到數據變化,就算模擬自定義事件,也只能在當前頁面中獲得事件,無法跨頁面通信。以下代碼只能在本頁面中監聽 sessionStorage 數據改變:

(() => {
  // 移除所有會話存儲
  sessionStorage.clear();
  
  // 監聽 storage 事件
  window.addEventListener('storage', e => {
    console.log(e);
  })

  // 自定義寫入函數,封裝事件派發
  function setItem (key, value) {
    // 模擬 storage 事件
    const ev = new StorageEvent('storage', {
      key,
      newValue: value,
      oldValue: sessionStorage.getItem(key),
      url: location.href,
    });
    window.dispatchEvent(ev);
    sessionStorage.setItem(key, value);
  }

  setItem('type', '公眾號');
  setItem('name', '前端路引');
})()

測試結果:

84-2

新開頁面複製 sessionStorage

a 標籤中添加 rel="opener" 可令新開的頁面獲得父頁面中的 sessionStorage 數據(複製方式)。

window.open 方法打開的頁面也是一樣的道理。

注意:此時獲得數據僅僅是副本,不共享,所以在新頁面或舊頁面中修改數據時,兩者互不影響。

a 標籤默認是 noopener,如果不顯示設置 rel="opener",無法獲得數據副本!!

測試代碼:

<a
  href="./example-84-3.html"
  target="_blank"
>a 標籤打開本頁面</a><br><br>
<a
  href="./example-84-3.html"
  target="_blank"
  rel="opener"
>a 標籤 opener 打開本頁面</a><br><br>
<a
  href="./example-84-3.html"
  target="_blank"
  rel="noopener"
>a 標籤 noopener 打開本頁面</a><br><br>

<button
  onclick="window.open('./example-84-3.html')"
>window.open 打開本頁面</button><br><br>
<button
  onclick="window.open('./example-84-3.html', '_blank', 'noopener')"
>window.open noopener 打開本頁面</button><br><br>

<button id="set">寫入新的 sessionStorage</button><br><br>
<button id="get">獲取 sessionStorage</button><br><br>

<div id="output"></div>

<script>
  (() => {
    const output = document.querySelector('#output');
    document.querySelector('#set').addEventListener('click', e => {
      sessionStorage.setItem('now', Date.now());
    })

    document.querySelector('#get').addEventListener('click', e => {
      const now = sessionStorage.getItem('now');
      output.innerText = now ?? '無';
    })
  })()
</script>

測試結果:

84-3

大小限制

localStorage 一樣限制 5MB 大小(所有鍵值加在一起的長度),溢出後寫入報錯,此處推薦閲讀前一篇文章:{%post_link 'wechat-web-front-end-83'%}

測試代碼:

(() => {
  // 移除所有會話存儲
  sessionStorage.clear();
  const key = 'name';
  const max = 5 * 1024 * 1024;
  // 測試極限值
  // const value = 'a'.repeat(max - key.length);
  // 測試中文
  // const value = '中'.repeat(max - key.length);
  // 測試溢出
  const value = 'a'.repeat(max - key.length + 1);
  sessionStorage.setItem(key, value);
  // 測試多個項溢出情況
  // sessionStorage.setItem('key', '1');
  // 測試與 localStorage 是否共用存儲空間
  // localStorage.setItem('key', '1');
})()

溢出結果:

84-4

結論:setItem 多個子項時,公用 5MB 存儲空間(中英文計算方式一致),溢出時寫入報錯,並且與 localStorage 不共用存儲空間!使用 rel="opener" 打開鏈接時,會獲得父頁面的副本數據,所以新開的頁面僅能寫入 5MB - 副本數據 等到的剩下空間。

可使用以下代碼判斷是否擁有副本數據:

if (window.opener) {
  // opener 存在表示擁有副本
  // 極限情況再寫入新的數據報錯
  sessionStorage.setItem('now', Date.now());
}

刷新與恢復頁面

頁面刷新時和關閉頁面後再使用 ctrl+shift+t 恢復頁面,sessionStorage 數據不會丟失。

測試代碼:

<button id="set">寫入新的 sessionStorage</button><br><br>
<button id="get">獲取 sessionStorage</button><br><br>

<div id="output"></div>

<script>
  (() => {
    const output = document.querySelector('#output');
    document.querySelector('#set').addEventListener('click', e => {
      sessionStorage.setItem('now', Date.now());
    })

    document.querySelector('#get').addEventListener('click', e => {
      const now = sessionStorage.getItem('now');
      output.innerText = now ?? '無';
    })
  })()
</script>

效果:

84-5

寫在最後

sessionStorage 僅支持字符串存儲,所以 JS 中用的 JSON 數據需要格式化為字符串存儲~~

sessionStorage 一般多用於臨時數據存儲,比如一些表單填寫的臨時數據,單頁應用頁面間的數據傳遞等。

其生命週期有點短暫,瀏覽器或標籤頁關閉就會消失,就像浮游一樣,朝生暮死...

user avatar Leesz Avatar alibabawenyujishu Avatar littlelyon Avatar inslog Avatar banana_god Avatar huichangkudelingdai Avatar u_17443142 Avatar xiaoxxuejishu Avatar zhulongxu Avatar weidewei Avatar kitty-38 Avatar joe235 Avatar
Favorites 76 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.