深入了解 Web Storage API
Web Storage API 是浏览器内置的键值对(key/value)存储机制,包括 sessionStorage
和 localStorage
。两者同属 Storage
接口,方法名相同,只是存储数据的有效期不同。
sessionStorage
的数据在页面会话(page session)期间有效。只要标签页不关闭,刷新页面或页面回退,数据都不消失。如果关闭页面,数据就会消失。
localStorage
的数据更持久,即使关闭页面也不会消失。
出于安全考虑,不同源(origin)之间的 Storage 数据相互独立。
基本用法
localStorage
和 sessionStorage
常用的方法包括:
setItem(key, value)
设定键名对应的值getItem(key)
读取键名对应的值,如果为空,返回null
removeItem(key)
移除某个键值对clear()
移除所有键值对
两者只能存储字符串类型的数据,如果你想存储复杂的数据结构,需要提前转换为字符串,比如使用 JSON.stringify()
方法。
它俩还有一个不太常用的方法 key(index)
,可以返回指定索引的键名。有一个不常用的属性 length
,可以获取当前的键值对个数。
因此,如果想读取当前 origin 的所有键名,可以这么写:
function getStorageKeys(storage) {
return Array(storage.length).fill('').map((_, i) => storage.key(i))
}
getStorageKeys(window.localStorage)
getStorageKeys(window.sessionStorage)
storage 事件
当某个页面的 localStorage
数据发生变化,其他页面的 window
会监听到 storage
事件。这个事件常用于本地缓存数据的同步。
这里有两点需要注意:
sessionStorage
的数据变化不会触发storage
事件- 引发
localStorage
数据变化的页面,监听不到本次改变触发的storage
事件
这个事件的类型是 StorageEvent
,它有如下属性:
key
:受影响的键名newValue
:数据新值oldValue
:数据旧值storageArea
:受影响的Storage
实例,即localStorage
url
:引起数据变化的页面地址
window.addEventListener('storage', (e) => {
console.log('键名', e.key)
console.log('新值', e.newValue)
console.log('旧值', e.oldValue)
console.log('localStorage', e.storageArea)
console.log('哪个引发的变化', e.url)
})
数据限额
根据 MDN 的描述,浏览器对于 Web Storage 的数据限额是 10MiB,其中 sessionStorage
占 5MiB,localStorage
占据 5MiB。
注意,上述限额针对的是同源下的数据限额,不同源之间数据隔离,互不影响,不会叠加。
如果数据达到存储上限,继续增加数据,会触发 QuotaExceededError
异常。
如果你对 MDN 的说法存疑,最好动手检验一下。
首先,访问任意页面,启动开发者工具,切换到【控制台】标签。下面的操作均在【控制台】中进行。
使用字符串的 repeat()
方法,生成 1MiB 的字符串常量。
const mib = 'a'.repeat(1024 * 1024)
然后,清空本地缓存,依次手动写入多条缓存数据。观察是否抛出异常。
// 清空本地缓存
localStorage.clear()
localStorage.setItem('1', mib)
localStorage.setItem('2', mib)
localStorage.setItem('3', mib)
localStorage.setItem('4', mib)
localStorage.setItem('5', mib) // <- 进行到这里将抛出异常
为什么还没到 5MiB,刚过 4MiB 就报错了?因为键名也会消耗数据限额,上面的 5 个键名总共占据 5 个字节。
如果想榨干 5MiB 的每一个字节,需要从最后一个 mib
字符串中剔除 5 个键名占用的空间:
localStorage.setItem('5', mib.slice(0, -5)) // 这次正常执行
此后,再增加一个字节,都会触发配额超限的异常 QuotaExceededError
。
看来,MDN 说的是真的。
完