浏览器存储:localStorage / sessionStorage / cookie 应该怎么用

0 阅读8分钟

同学们好,我是 Eugene(尤金),一个拥有多年中后台开发经验的前端工程师~

(Eugene 发音很简单,/juːˈdʒiːn/,大家怎么顺口怎么叫就好)

你是否也有过:明明学过很多技术,一到关键时候却讲不出来、甚至写不出来?

你是否也曾怀疑自己,是不是太笨了,明明感觉会,却总差一口气?

就算想沉下心从头梳理,可工作那么忙,回家还要陪伴家人。

一天只有24小时,时间永远不够用,常常感到力不从心。

技术行业,本就是逆水行舟,不进则退。

如果你也有同样的困扰,别慌。

从现在开始,跟着我一起心态归零,利用碎片时间,来一次彻彻底底的基础扫盲

这一次,我们一起慢慢来,扎扎实实变强。

不搞花里胡哨的理论堆砌,只分享看得懂、用得上的前端干货,

咱们一起稳步积累,真正摆脱“面向搜索引擎写代码”的尴尬。

一、开篇:为什么要关心浏览器存储?

日常开发里经常会遇到:

  • Token 存哪:登录后 token 放在哪、刷新页面怎么不丢?
  • 用户配置:主题、语言、侧边栏收起状态,下次打开要记住。
  • 列表筛选:表格的筛选条件、分页、排序,希望刷新后还能保留。

这些都要用到浏览器存储。但 localStorage、sessionStorage、cookie 该怎么选?场景对应错了,就会有跨 tab 不同步、隐私泄露、存不下等问题。

下面从概念 → 用法 → 实战场景 → 选型 → 踩坑,把三者讲清楚:日常该用哪个、为什么这么用、容易踩哪些坑

二、概念扫盲

先用一张表看清三者区别:

特性localStoragesessionStoragecookie
生命周期持久化,除非手动删除关闭标签页即失效可设置过期时间
容量约 5MB约 5MB约 4KB
作用域同源下所有页面共享仅当前标签页同源共享,可设置 path
是否随请求发送是(每次请求自动带)
API 风格getItem / setItem同上document.cookie 字符串操作

一句话记:

  • localStorage:长期存、全页面共享,适合偏好、配置。
  • sessionStorage:仅当前标签页、关掉就丢,适合临时状态。
  • cookie:每次请求都会带上,适合需和服务端交互的(如认证、会话)。

三、localStorage:持久化存储

3.1 是什么?

localStorage 是持久化存储,数据不会过期,除非主动删除,或用户清除浏览器数据。同源下所有页面共享。

3.2 基本用法

// 存
localStorage.setItem('theme', 'dark');

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

// 删
localStorage.removeItem('theme');

// 清空所有
localStorage.clear();

注意:只能存字符串。对象需要序列化:

// 存对象
const userConfig = { theme: 'dark', lang: 'zh' };
localStorage.setItem('userConfig', JSON.stringify(userConfig));

// 取对象
const config = JSON.parse(localStorage.getItem('userConfig') || '{}');

3.3 适用场景

  • 用户主题、语言、侧边栏状态
  • 需要持久化的列表筛选、排序、分页
  • 表单草稿(不敏感数据)
  • Token(前提是放在合理的安全体系里)

四、sessionStorage:会话级存储

4.1 是什么?

sessionStorage 的生命周期只到当前标签页关闭。新开标签页是新会话,数据不共享。

4.2 基本用法

// 用法和 localStorage 一样
sessionStorage.setItem('tempId', 'abc123');
const tempId = sessionStorage.getItem('tempId');
sessionStorage.removeItem('tempId');

4.3 适用场景

  • 当前页的临时数据(如多步表单的中间页)
  • 不希望跨 tab 共享的数据
  • 表单防重复提交用的临时标记
  • 一些只对当前会话有用的临时 token

4.4 常见误区

很多人以为 sessionStorage 和“会话”一样,关浏览器才清空。实际上:

  • 关闭标签页 → 数据就没了
  • 刷新页面 → 数据还在
  • 新开标签页 → 新会话,看不到旧数据

五、cookie:会被请求携带的存储

5.1 是什么?

cookie 是早期方案,容量小,每次 HTTP 请求会自动带上(同域且 path 匹配时)。因此常用来存 sessionId、token 等需要和服务端交互的信息。

5.2 基本用法

cookie 需要手动拼字符串,原生 API 不友好:

// 设置 cookie(需手动拼接)
document.cookie = 'token=abc123; path=/; max-age=3600';  // 1小时过期

// 读取 cookie
const cookies = document.cookie;  // "token=abc123; theme=dark"
// 需要自己解析
function getCookie(name) {
  const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
  return match ? match[2] : null;
}
getCookie('token');  // 'abc123'

常用属性说明:

属性含义
path=/哪些路径会携带此 cookie
max-age=3600多少秒后过期
domain=.example.com子域名共享
Secure仅 HTTPS 传输
HttpOnly禁止 JS 访问,防 XSS 读 token

5.3 适用场景

  • 登录凭证(sessionId、token),尤其是需要服务端校验时
  • 与后端交互的会话状态
  • 老项目或接口强依赖 cookie 的场景

现代项目里,很多 token 用 localStorage 存,请求时通过 Authorization 头发送,而不再用 cookie。这取决于你的前后端约定和安全方案。

六、实战场景:该用哪个?

6.1 Token 存储

常见做法有三种:

方式优点缺点
localStorage刷新不丢、跨 tab 共享、实现简单易受 XSS 攻击读走 token
sessionStorage关 tab 即失效,相对更安全刷新会丢,需配合刷新 token 逻辑
cookie (HttpOnly)JS 无法读取,防 XSS 偷 token需后端配合设置,前后端都要处理

建议

  • 大部分前后端分离项目:用 localStorage,配合 XSS 防护(输入过滤、CSP 等)。
  • 对安全要求高的项目:用 HttpOnly cookie,由后端设置。
  • 仅当前 tab 有效的临时 token:用 sessionStorage

示例(localStorage 存 token):

// 登录成功后
const token = response.data.token;
localStorage.setItem('token', token);

// 请求拦截器里
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 退出登录
localStorage.removeItem('token');

6.2 用户配置(主题、语言等)

这类配置需要持久化,且希望下次打开还是同样的,用 localStorage

// 封装一个简单的配置管理
const CONFIG_KEY = 'userConfig';

const defaultConfig = {
  theme: 'light',
  lang: 'zh',
  sidebarCollapsed: false
};

function getConfig() {
  try {
    const saved = localStorage.getItem(CONFIG_KEY);
    return saved ? { ...defaultConfig, ...JSON.parse(saved) } : { ...defaultConfig };
  } catch {
    return { ...defaultConfig };
  }
}

function setConfig(partial) {
  const config = { ...getConfig(), ...partial };
  localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
  return config;
}

// 使用
setConfig({ theme: 'dark' });
const config = getConfig();

6.3 缓存查询条件(表格筛选、分页)

列表的筛选、排序、分页希望刷新后还能保留,用 localStorage

const CACHE_KEY = 'userList_query';

// 进入页面时恢复
const savedQuery = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');
this.filters = savedQuery.filters || {};
this.pagination = savedQuery.pagination || { page: 1, size: 10 };

// 筛选或分页变化时保存
function saveQuery(filters, pagination) {
  localStorage.setItem(CACHE_KEY, JSON.stringify({
    filters,
    pagination
  }));
}

// 清空筛选时清除
function clearQuery() {
  localStorage.removeItem(CACHE_KEY);
}

6.4 多步表单的中间状态

多步表单的中间页,只对当前 tab 有效,不希望跨 tab 共享,用 sessionStorage

// 第二步填写时保存
sessionStorage.setItem('formStep2', JSON.stringify(formData));

// 第三步页面加载时恢复
const step2Data = JSON.parse(sessionStorage.getItem('formStep2') || '{}');

// 提交成功后清除
sessionStorage.removeItem('formStep2');

七、怎么选?一张表搞定

场景推荐理由
TokenlocalStorage 或 HttpOnly cookie持久、跨 tab;安全要求高用 cookie
用户主题、语言、配置localStorage持久化,刷新和重开都保留
列表筛选、分页、排序localStorage持久化,提升使用体验
多步表单中间状态sessionStorage仅当前 tab,关掉即清空
临时防重复提交标记sessionStorage仅当前会话有效
需要随请求自动携带的凭证cookie浏览器会自动带上

八、踩坑指南

原因建议
存对象报错或乱码只支持字符串JSON.stringify 存,JSON.parse
私密模式下 localStorage 可能不可用部分浏览器会限制或清空用 try-catch 包一层,失败时降级到内存
跨 tab 不同步localStorage 变更不会自动通知其他 tab监听 storage 事件做同步
cookie 超出 4KB单条 cookie 约 4KB 限制大对象改用 localStorage,或拆分多个 cookie
测试环境数据残留上次测试数据还在测试前 localStorage.clear() 或单独用测试 key

storage 事件:跨 tab 同步

当其他 tab 修改了 localStorage,当前 tab 会触发 storage 事件:

window.addEventListener('storage', (e) => {
  if (e.key === 'theme') {
    // 其他 tab 改了 theme,这里同步更新
    applyTheme(e.newValue);
  }
});

注意:修改 localStorage 的那个 tab 不会触发,只有其他同源页面会收到。


九、小结

存储方式一句话典型场景
localStorage持久、同源共享、不随请求发送用户配置、token、列表筛选缓存
sessionStorage当前标签页有效、关掉即丢多步表单、临时标记
cookie容量小、随请求自动携带需后端校验的登录凭证

记住三点:

  1. 需要持久化、跨 tab 用 localStorage;仅当前 tab、关掉就丢 用 sessionStorage。
  2. 需要随请求自动携带 用 cookie;否则优先用 localStorage/sessionStorage。
  3. 只存字符串,对象用 JSON.stringify/parse;注意容量(localStorage 约 5MB,cookie 约 4KB)。

选对存储方式,可以少很多莫名其妙的 bug,也更符合安全和性能预期。


学习本就是一场持久战,不需要急着一口吃成胖子。哪怕今天你只记住了一点点,这都是实打实的进步。

后续我还会继续用这种大白话、讲实战方式,带大家扫盲更多前端基础。

关注我,不迷路,咱们把那些曾经模糊的知识点,一个个彻底搞清楚。

如果你觉得这篇内容对你有帮助,不妨点赞收藏,下次写代码卡壳时,拿出来翻一翻,比搜引擎更靠谱。

我是 Eugene,你的电子学友,我们下一篇干货见~