Web前端入門第 84 問:JavaScript sessionStorage 那些容易踩坑的地方
sessionStorage 與 localStorage 差不多可以算作一對兄弟,它倆的暴露的 API 方法一模一樣。
但兩者也有不同點:
1、sessionStorage 存入的數(shù)據(jù)在頁面關(guān)閉后,會自動清除。
2、相同 URL 的每個 tab 頁簽的 sessionStorage 會被隔離,互不影響。也就是說相同的鏈接,在 A 標簽頁打開和在 B 標簽打開,A 寫入的 sessionStorage 數(shù)據(jù),B 是無法讀取的!!這里與 會話 Cookie 完全不一樣。
3、在頁面中使用 <a target="_blank" rel="opener"> 和 window.open 打開新的標簽頁,sessionStorage 數(shù)據(jù)會復(fù)制,但之后的修改相互隔離。
4、sessionStorage 存入的數(shù)據(jù)會在瀏覽器打開期間一直保留,刷新頁面或使用 ctrl + shift + t 恢復(fù)頁面時,數(shù)據(jù)不會丟失。
API
sessionStorage 暴露的接口與 localStorage 一致:
// 只讀屬性,返回會話存儲項的數(shù)目
sessionStorage.length
// 獲取會話存儲的第 n 個鍵名
sessionStorage.key(n)
// 獲取會話存儲的值
sessionStorage.getItem(key)
// 寫入會話存儲
sessionStorage.setItem(key, value)
// 刪除會話存儲
sessionStorage.removeItem(key)
// 清空所有會話存儲
sessionStorage.clear()
API 使用示例:
(() => {
// 寫入數(shù)據(jù)
sessionStorage.setItem('type', '公眾號');
sessionStorage.setItem('name', '前端路引');
// 獲取會話存儲長度
console.log(sessionStorage.length);
// 循環(huán)打印會話存儲
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();
})()
無法監(jiān)聽存儲數(shù)據(jù)變化
不像 localStorage,sessionStorage 沒有暴露 onchange 事件,所以無法監(jiān)聽到數(shù)據(jù)變化,就算模擬自定義事件,也只能在當(dāng)前頁面中獲得事件,無法跨頁面通信。以下代碼只能在本頁面中監(jiān)聽 sessionStorage 數(shù)據(jù)改變:
(() => {
// 移除所有會話存儲
sessionStorage.clear();
// 監(jiān)聽 storage 事件
window.addEventListener('storage', e => {
console.log(e);
})
// 自定義寫入函數(shù),封裝事件派發(fā)
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', '前端路引');
})()
測試結(jié)果:

新開頁面復(fù)制 sessionStorage
在 a 標簽中添加 rel="opener" 可令新開的頁面獲得父頁面中的 sessionStorage 數(shù)據(jù)(復(fù)制方式)。
window.open 方法打開的頁面也是一樣的道理。
注意:此時獲得數(shù)據(jù)僅僅是副本,不共享,所以在新頁面或舊頁面中修改數(shù)據(jù)時,兩者互不影響。
a 標簽?zāi)J是 noopener,如果不顯示設(shè)置 rel="opener",無法獲得數(shù)據(jù)副本!!
測試代碼:
<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>
測試結(jié)果:

大小限制
與 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');
})()
溢出結(jié)果:

結(jié)論:setItem 多個子項時,公用 5MB 存儲空間(中英文計算方式一致),溢出時寫入報錯,并且與 localStorage 不共用存儲空間!使用 rel="opener" 打開鏈接時,會獲得父頁面的副本數(shù)據(jù),所以新開的頁面僅能寫入 5MB - 副本數(shù)據(jù) 等到的剩下空間。
可使用以下代碼判斷是否擁有副本數(shù)據(jù):
if (window.opener) {
// opener 存在表示擁有副本
// 極限情況再寫入新的數(shù)據(jù)報錯
sessionStorage.setItem('now', Date.now());
}
刷新與恢復(fù)頁面
頁面刷新時和關(guān)閉頁面后再使用 ctrl+shift+t 恢復(fù)頁面,sessionStorage 數(shù)據(jù)不會丟失。
測試代碼:
<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>
效果:

寫在最后
sessionStorage 僅支持字符串存儲,所以 JS 中用的 JSON 數(shù)據(jù)需要格式化為字符串存儲~~
sessionStorage 一般多用于臨時數(shù)據(jù)存儲,比如一些表單填寫的臨時數(shù)據(jù),單頁應(yīng)用頁面間的數(shù)據(jù)傳遞等。
其生命周期有點短暫,瀏覽器或標簽頁關(guān)閉就會消失,就像浮游一樣,朝生暮死...
文章首發(fā)于微信公眾號【前端路引】,歡迎 微信掃一掃 查看更多文章。
本文來自博客園,作者:前端路引,轉(zhuǎn)載請注明原文鏈接:http://www.rzrgm.cn/linx/p/19047887

浙公網(wǎng)安備 33010602011771號