跳转到内容

localStorage 与 sessionStorage

发布于 更新于

一句话: localStorage 与 sessionStorage 都是暴露在 Window 上的同步、仅存字符串、 同源的键值存储;localStorage 会跨标签页和浏览器重启持久保存,而 sessionStorage 只作用于 单个标签页,在该标签页关闭时被清除。

localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");
localStorage.clear(); // 移除该源下的所有键
// sessionStorage 拥有完全相同的方法集,作用范围为当前标签页:
sessionStorage.setItem("draftId", "42");

根据 MDN 的使用指南,两个接口都只存储字符串——对象或数组等非字符串数据必须显式转换, 例如使用 JSON.stringify()。

localStorage 与 sessionStorage 都是 Web Storage API 中历史悠久、被广泛实现的部分, 在当前主流浏览器中均可用。根据 MDN,二者是各自独立的 Storage 对象,“function and are controlled separately”(各自独立运作和控制)。根据 MDN 的使用指南,当该源可用的存储空间 全部用尽后,写入仍可能失败,此时会抛出 QuotaExceededError 这一 DOMException,而不是 静默失败。

window.addEventListener("storage", (event) => {
console.log(event.key, event.oldValue, event.newValue);
});

storage 事件会在 localStorage 发生变化时,触发在共享同一存储区域的其他 Document 上——绝不会触发在发起变更的那个 document 自身。根据 MDN,这意味着同一标签页中其他同源的 document(例如一个 iframe)仍可能收到该事件,即使发起写入的 document 不会。可用它来保持 其他已打开的 document 之间的同步。

function storageAvailable() {
if (!('localStorage' in window)) {
// localStorage 属性不存在 —— 回退到本次会话的内存存储。
return false;
}
try {
const probe = "__storage_test__";
window.localStorage.setItem(probe, probe);
window.localStorage.removeItem(probe);
return true;
} catch {
// 接口存在但不可用——例如某些隐私浏览模式会报告 0 字节配额——因此视为
// 不可用,并采用相同的回退方式。
return false;
}
}

这与 MDN 自身在“Using the Web Storage API”中给出的特性检测方法类似,同样通过一次真实 写入来探测——不过 MDN 的版本会把已有数据的存储上抛出的 QuotaExceededError 视为存储 仍然可用的证据,而不是像上面这个简化探测那样,一旦捕获异常就无条件判定为不可用。

  • 值始终是字符串——存入结构化数据前先用 JSON.stringify(),取出后用 JSON.parse()。
  • 两者都是同源的:协议、主机、端口必须全部一致,因此 https://example.com 与 http://example.com 不共享存储。
  • storage 事件只会在共享存储区域的其他 document 上触发,绝不会在发起写入的那个 document 上触发——不要在本地等待它。根据 MDN,同一标签页中其他同源的 document (例如一个 iframe)仍可能收到该事件。
  • 隐私/无痕模式下的行为在各浏览器间并不一致。根据 MDN 的 Web Storage 指南, 在隐私窗口中写入的数据通常会被当作 sessionStorage 处理,在最后一个隐私 标签页关闭时被清除——但 MDN 同时指出,部分浏览器会改为暴露一个配额固定为 0 字节的 localStorage 对象,写入会立即抛出,而不是保留到标签页关闭。 应通过真实的写入/删除探测来做特性检测,而不是假设某一种行为。
  • 所有读写都是同步的,并运行在主线程上;较大的值或频繁写入会阻塞渲染—— 对于体量较大的数据应优先使用 IndexedDB。
  • 根据 MDN,sessionStorage 是按源与顶层浏览上下文共同划分的——并不只是简单地 “每个标签页一份”。根据 MDN,一个持有 opener(指向打开它的那个页面)引用的 新页面,在初始时会获得该 opener 的 sessionStorage 副本;没有 opener 的 上下文则不会。