前端本地存储怎么选:Cookie、localStorage、sessionStorage、IndexedDB 的取舍

全文约2200字,预计阅读8分钟。

页面要记住登录态、要缓存一份列表、要存一段用户的草稿,下意识就是"写个本地存储吧"。但浏览器里能存东西的地方不止一个,Cookie、localStorage、sessionStorage、IndexedDB 各有各的脾气,用错了轻则数据存不进去,重则把隐私模式下的页面直接搞崩。这篇把四种常见方案的容量、生命周期、用法和坑串一遍,附上代码和踩坑记录,下次选型时照着对号入座就行。

01 前言:先搞清楚你要存什么

很多选型纠结,根源是没先把"要存的东西"分清楚。动笔写代码之前,先问三个问题:

  • 存多少:是几十字节的一个标记,还是几十兆的结构化数据?
  • 存多久:是关掉标签页就该没,还是下次打开还要在?
  • 存给谁用:只是前端自己读,还是每次请求都要带给服务端?

这三个问题的答案不同,合适的方案完全不一样。先分类,再挑工具,能省掉一半返工。

02 四种方案先对号入座

方案 容量 生命周期 是否随请求自动带上 适合存什么
Cookie 约4KB 可设置过期时间 是,每次同域请求都带 登录态标识、需要服务端读到的标记
localStorage 数MB级 持久,手动才清 否 前端自己用的偏好、缓存
sessionStorage 数MB级 标签页关闭即清 否 单次会话内的临时数据
IndexedDB 数百MB以上 持久,手动才清 否 结构化数据、离线缓存、大量记录

一句话记忆:要带给服务端的小标记用 Cookie;前端自己长期存的小数据用 localStorage;只在当前会话有用的用 sessionStorage;数据量大、要按条件查的用 IndexedDB。

03 Cookie:小,但会跟着请求跑

Cookie 最大的特点不是能存东西,而是每次同域请求都会自动带上它。这既是它的用处,也是它的坑。

写入和读取:

js 复制代码
// 写入,path 与 expires 决定生效路径和过期时间
document.cookie = 'token=abc123; path=/; max-age=604800; SameSite=Lax'

// 读取是一整串,需要自己拆
document.cookie.split('; ').reduce((obj, item) => {
  const [k, v] = item.split('=')
  obj[k] = v
  return obj
}, {})

要点:

  • 容量只有 4KB 左右,别想着往里塞用户信息、塞列表,塞不下还会把每个请求都拖慢。
  • 敏感字段加 HttpOnly 和 Secure:能被 JS 读到的 Cookie,就有被脚本偷走的风险。真正的登录凭证,更适合放在 HttpOnly 的 Cookie 里,JS 根本读不到。
  • SameSite 要显式设:不设的话跨站场景下浏览器的默认策略可能和你预期不一致,导致登录态莫名丢失。

04 localStorage 与 sessionStorage:一对双胞胎

这俩用法几乎一模一样,区别只在生命周期:localStorage 关掉浏览器还在,sessionStorage 关掉当前标签页就没了。

js 复制代码
// 存(注意只能存字符串)
localStorage.setItem('theme', 'dark')

// 取
const theme = localStorage.getItem('theme')

// 删
localStorage.removeItem('theme')

存对象要先序列化 :它们只能存字符串,直接塞对象会变成 [object Object],这是新手最常踩的坑。

js 复制代码
// 正确做法:JSON 转一道
localStorage.setItem('user', JSON.stringify({ name: '小明', role: 'admin' }))

// 读出来再 parse,记得兜一层 try/catch
let user = null
try {
  user = JSON.parse(localStorage.getItem('user'))
} catch (e) {
  // 存的内容被改坏了,别让整个页面挂掉
}

两者怎么选:要长期保留的用户偏好、最近一次的筛选条件,用 localStorage;只是填了一半的表单、当前向导走到第几步,关掉页面就该清空的,用 sessionStorage。

05 IndexedDB:数据多了再上

当你要存几十上百条记录、还要按条件查询,localStorage 就力不从心了------它本质是个键值表,查一条得把整坨 JSON 读出来再自己过滤。这时候该上 IndexedDB。

它是浏览器内置的事务型数据库,API 偏底层,写起来啰嗦一点,但能力和容量都大得多。一个最薄的封装长这样:

js 复制代码
function openStore(dbName, storeName) {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(dbName, 1)
    req.onupgradeneeded = () => {
      const db = req.result
      if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName, { keyPath: 'id' })
      }
    }
    req.onsuccess = () => resolve(req.result)
    req.onerror = () => reject(req.error)
  })
}

async function put(store, value) {
  const tx = store.transaction ? store : null
  // 实际项目里按 db.transaction 开读写事务再 add/put
}

要点:

  • 它是异步的,API 基于回调和请求对象,不熟悉的话建议包一层 Promise 再用,别在业务里直接写一堆 onsuccess。
  • 有事务和索引,适合"一批记录、能按某个字段查"的场景,比如离线缓存的文章列表、本地草稿箱。
  • 别为了存一两个字段就上 IndexedDB,杀鸡用牛刀,徒增维护成本。

06 选型判断流程

不知道用哪个时,按这个顺序问自己:

  1. 这个数据需不需要每次请求都带给服务端?要 → Cookie;
  2. 数据量是不是几KB以内、纯前端自己用?是 → 长期用 localStorage,单次会话用 sessionStorage;
  3. 数据量大、需要结构化查询?是 → IndexedDB。

实际项目里常见的组合是:登录凭证放 HttpOnly Cookie,界面偏好放 localStorage,离线缓存的列表走 IndexedDB。各管一摊,别互相串。

07 踩坑记录

踩坑一:存对象没序列化,读出来是 object Object。 修复:凡是存对象,一律 JSON.stringify 进去、JSON.parse 出来,并且 parse 外面包 try/catch。

踩坑二:隐私模式下写入直接报错。 很多浏览器在无痕/隐私模式下会限制或禁掉本地存储,setItem 可能直接抛异常。修复:所有写入都包一层容错,存不进去就降级成内存变量,别让脚本崩在头一行。

踩坑三:存超容量被截断或抛错。 localStorage 不是无限大,塞太多会触发配额异常。修复:写入前做防御,catch 到配额错误时清理一批旧数据再重试,而不是直接把页面搞白。

踩坑四:storage 事件被滥用。 storage 事件只在"其他标签页"修改存储时触发,当前页自己改不会触发。想跨页通信是对的,但别指望它监听自己这一页的变化。

踩坑五:把敏感信息明文塞进 localStorage。 localStorage 能被同域脚本任意读取,一旦有 XSS,里面的东西就等于公开。敏感、能冒充身份的信息别放这儿,放 HttpOnly Cookie 更稳。

踩坑六:以为 sessionStorage 能跨标签页共享。 它是跟当前标签页绑定的,新开一个标签页就是另一份。需要跨页共享的,得用 localStorage 或广播机制。

08 总结

前端本地存储没有万能方案,按数据形态分:

  1. 要带给服务端、量小的标记 → Cookie,敏感的加 HttpOnly;
  2. 前端长期存的小数据 → localStorage;
  3. 只在当前会话有效的临时数据 → sessionStorage;
  4. 大量结构化、要查询的数据 → IndexedDB。

记住两条底线:存对象先序列化、写入永远包容错;敏感身份信息别交给能被 JS 读的地方。把这几条守住,日常项目里的本地存储基本不会出大问题。选型时多花十秒钟想清楚"存多少、存多久、给谁用",比后面对着报错调半天强得多。

相关推荐
用户5508492902561 小时前
Git 日常命令与分支协作流程:从提交到合并的实战梳理
前端
修炼的dance1 小时前
同一段视频没降画质为什么就快了
前端
asong1 小时前
从写代码到部署上线,Cloudflare 给 AI 配了一把新钥匙
前端·javascript·后端
用户5372312882461 小时前
你的 WGSL 从未被执行过——一个 secure context 静默陷阱的排查实录
前端
Daniel_1232 小时前
轻量级站点监控面板 LightPing 开源啦
前端·后端·github
想风2 小时前
A/B 测试怎么做?独立站转化率优化的完整实操步骤
前端·后端·github
lerhxx2 小时前
从 Markdown 到生成式 UI:AI 应用中的流式渲染实践
前端·javascript
迅猛龙办公室2 小时前
python实现简单进度条
java·前端·python
Rosanci2 小时前
Codex 下载与本地部署实战:从安装到运行全流程指南
开发语言·前端·算法·chatgpt·codex