跳转到内容

存储 · API

localStorage 与 sessionStorage

发布于 更新于

window.localStorage 与 window.sessionStorage 是同一源下两个只存字符串键值的 Storage 对象:localStorage 在关闭标签页和重启浏览器后仍在,sessionStorage 属于一个顶级浏览上下文,该标签页或窗口关闭即丢弃。两者都是同步的、只在主线程可用,worker 和 Service Worker 里没有,因此只适合存放小型标志和偏好,而不是应用数据。

localStorage.setItem(key, value)
localStorage.getItem(key)
localStorage.removeItem(key)
localStorage.clear()
localStorage.key(index)
localStorage.length

sessionStorage 的接口完全相同。值以字符串存储:setItem('n', 1) 存入 "1",对象不先序列化就会存成 "[object Object]"。Chrome 4、Firefox 3.5、Safari 4 起支持(BCD api.Window.localStorage);没有安全上下文要求,但同一主机的两种 scheme 是不同的源,互不共享存储。

方法 参数 类型 含义
setItem key、value string、string 创建或覆盖一条。非字符串参数经 String() 转换。
getItem key string 返回值,键不存在时返回 null。空字符串是合法的存储值,返回 "" 而不是 null。
removeItem key string 删除一条。删除不存在的键不报错。
clear 无 删除该源在该存储区内的全部条目。
key index unsigned long 返回该位置的键,越界返回 null。顺序由实现决定,写入后会变。

每次调用都在主线程同步执行;Firefox 和 Chromium 用按源划分的磁盘数据库支撑它,循环写入大字符串会在整个过程中阻塞渲染。

Web Storage 存在于所有引擎(Chrome 4、Firefox 3.5、Safari 4;BCD api.Window.localStorage),包括 Android WebView 和 iOS 的 WebView,是唯一没有支持缺口的客户端存储。缺口在于上下文而非浏览器:worker 和 Service Worker 不暴露这两个对象,不透明源访问即抛出,每源上限也很小。

异常 位置 触发条件
QuotaExceededError DOMException setItem() 写入将超出存储区上限。Firefox 把 localStorage 限制为每源 5 MiB(StaticPrefList.yaml 中的 dom.storage.default_quota,5 × 1024 KiB,github.com)。Chromium 采用类似的每源上限,报错文本为 Setting the value of '<key>' exceeded the quota.(storage_area.cc,chromium.googlesource.com)。
SecurityError DOMException 读取 window.localStorage 源是不透明源(没有 allow-same-origin 的沙箱 <iframe>、data: URL),或用户已为该站点拦截存储。getter 本身抛出,所以 'localStorage' in window 也不够,首次访问要包在 try 里。

在 Firefox 和 Chromium 中隐私窗口不是异常路径:写入成功,数据在隐私会话结束时丢弃。Safari 11 之前则暴露一个配额为零的 localStorage,第一次 setItem() 就抛出 QuotaExceededError;「写一条再删一条」的探测同时覆盖这两种行为。

示例使用 localStorage;换成 sessionStorage 即为按标签页的状态,代码其余部分不变。

保存偏好并在另一个标签页响应

Section titled “保存偏好并在另一个标签页响应”

storage 事件在共享该存储区的其他同源文档上触发,不在写入的那个文档上触发。用它让两个打开的标签页保持同步,而不必轮询。

function setTheme(theme) {
localStorage.setItem('theme', theme);
applyTheme(theme); // 本标签页不会收到 storage 事件
}
window.addEventListener('storage', (event) => {
if (event.storageArea === localStorage && event.key === 'theme') {
applyTheme(event.newValue ?? 'light'); // removeItem 之后 newValue 为 null
}
});

event.storageArea 区分 localStorage 和 sessionStorage 的写入,clear() 之后 event.key 为 null。

带体积守卫地序列化结构化数据

Section titled “带体积守卫地序列化结构化数据”

上限按序列化字符串的字符数计算,写入前先检查长度,并把 QuotaExceededError 当作「把数据搬到 IndexedDB」的信号而不是重试的信号。

function saveSettings(settings) {
const json = JSON.stringify(settings);
try {
localStorage.setItem('settings', json);
return true;
} catch (err) {
if (err.name === 'QuotaExceededError') return false; // 太大:改存 IndexedDB
throw err;
}
}

超过几百 KB 的数据应放进 IndexedDB,它是异步的,并共享该源大得多的配额。

探测写入并删除一个键。对不透明源(getter 抛出 SecurityError)、被拦截的站点和旧版 Safari 的零配额隐私模式都返回 false。

function storageAvailable(type) {
if (!(type in window)) {
return false; // 没有 Web Storage:本次会话把状态放在内存里
}
try {
const area = window[type];
const probe = '__probe__';
area.setItem(probe, probe);
area.removeItem(probe);
return true;
} catch {
return false; // 访问被拒或零配额:退回内存
}
}

启动时用 'localStorage' 调用一次并记住结果;它检测的条件在页面打开期间不会变化。

规范

规范状态
无。