浏览器
浏览器存储
一、总览对比
| 特性 | Cookie | localStorage | sessionStorage | IndexedDB |
|---|---|---|---|---|
| 容量 | ~4 KB | ~5 MB | ~5 MB | 数百 MB+ |
| 生命周期 | 可设过期时间 | 永久(手动清除) | 标签页关闭即清除 | 永久(手动清除) |
| 随请求发送 | ✅ 自动携带 | ❌ | ❌ | ❌ |
| 跨标签页共享 | ✅ | ✅ | ❌ | ✅ |
| 跨域访问 | 同域(可配 domain) | ❌ 同源 | ❌ 同源 | ❌ 同源 |
| 可被 JS 访问 | 可(除 HttpOnly) | ✅ | ✅ | ✅ |
| API 类型 | 字符串拼接 | 同步 Key-Value | 同步 Key-Value | 异步事务 |
| 适合存储 | 会话凭证、偏好 | 用户设置、缓存数据 | 单次会话临时数据 | 大量结构化数据 |
二、Cookie
HTTP 协议的一部分,每次请求自动携带,主要用于身份认证。
// 读写(字符串操作,较繁琐)
document.cookie = 'token=abc123; path=/; max-age=3600; Secure; SameSite=Strict'
document.cookie // 只读出未被 HttpOnly 保护的 cookie
// 删除:把 max-age 设为 0
document.cookie = 'token=; max-age=0'关键属性:
| 属性 | 作用 |
|---|---|
Expires / Max-Age | 过期时间,不设则为会话 cookie |
HttpOnly | 禁止 JS 读取,防 XSS 窃取 |
Secure | 仅 HTTPS 发送 |
SameSite | 防 CSRF:Strict / Lax / None |
Domain | 指定哪些域名可访问,默认当前域 |
Path | 指定哪些路径携带 cookie |
三、localStorage
同源页面永久共享,适合持久化用户偏好、非敏感缓存。
localStorage.setItem('theme', 'dark')
localStorage.getItem('theme') // 'dark'
localStorage.removeItem('theme')
localStorage.clear() // 清空所有
localStorage.length // key 的数量
localStorage.key(0) // 按下标取 key 名只能存字符串,对象需序列化:
localStorage.setItem('user', JSON.stringify({ name: 'Tom' }))
const user = JSON.parse(localStorage.getItem('user'))监听跨标签页变化:
window.addEventListener('storage', (e) => {
console.log(e.key, e.oldValue, e.newValue)
// 注意:当前标签页自己的修改不会触发此事件
})四、sessionStorage
与 localStorage API 完全相同,区别在于标签页关闭即清除,且不跨标签页共享。
sessionStorage.setItem('step', '2')
sessionStorage.getItem('step') // '2'适合:多步骤表单中间状态、单次会话的临时数据。
刷新页面数据仍在,只有关闭标签页(或标签页跳转到新 origin)才清除。
五、IndexedDB
浏览器内置的 NoSQL 数据库,支持大容量、结构化数据、索引查询,API 全部异步。
const request = indexedDB.open('myDB', 1)
request.onupgradeneeded = (e) => {
const db = e.target.result
db.createObjectStore('users', { keyPath: 'id' })
}
request.onsuccess = (e) => {
const db = e.target.result
// 写入
const tx = db.transaction('users', 'readwrite')
tx.objectStore('users').add({ id: 1, name: 'Tom' })
// 读取
const getTx = db.transaction('users', 'readonly')
getTx.objectStore('users').get(1).onsuccess = (e) => {
console.log(e.target.result) // { id: 1, name: 'Tom' }
}
}适合:离线应用、大量数据缓存(图片 blob、日志、本地数据库)。实际使用通常借助 idb 等库简化 API。
六、Storage 接口
localStorage 和 sessionStorage 都是 Storage 接口的实例,浏览器原生定义如下:
interface Storage {
readonly length: number
key(index: number): string | null
getItem(key: string): string | null
setItem(key: string, value: string): void
removeItem(key: string): void
clear(): void
}length:当前存储了多少个 keykey(n):按下标返回 key 名,配合length可遍历所有条目getItem/setItem/removeItem:增删改查- 值只能是字符串,传入非字符串会自动调用
.toString()
// localStorage 和 sessionStorage 是同一接口的两个不同实例
localStorage instanceof Storage // true
sessionStorage instanceof Storage // true
// 遍历所有 key
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
console.log(key, localStorage.getItem(key))
}Storage 本身不可直接 new,是浏览器内部实现并挂载到 window 上的。你无法创建自己的 Storage 实例,只能使用 window.localStorage 和 window.sessionStorage 这两个全局对象。
七、使用场景速查
| 场景 | 推荐方案 |
|---|---|
| 登录 token(安全) | HttpOnly Cookie |
| 主题 / 语言偏好 | localStorage |
| 多步骤表单草稿 | sessionStorage |
| 购物车(跨标签页同步) | localStorage + storage 事件 |
| 离线缓存 / 大文件 | IndexedDB |
| 敏感数据(密码等) | 不要存在任何客户端存储中 |