全面对比:cookie、localStorage 和 sessionStorage
1. Cookie
- 核心作用:用于浏览器和服务器通信(如身份验证)
- 存储大小:最多 4KB
- 生命周期:
- 可设置过期时间(通过
Expires或Max-Age) - 未设置则随浏览器会话结束失效
- 可设置过期时间(通过
- 访问方式:
- 读/写:
document.cookie(字符串形式) - 修改需手动字符串拼接
- 读/写:
- 通信特性:
- 每次 HTTP 请求自动携带(通过请求头)
- 增加网络负担
- 安全相关属性:
HttpOnly(禁止 JS 访问)Secure(仅 HTTPS 传输)SameSite(控制跨站发送)
- 典型场景:用户认证、会话跟踪
2. localStorage
- 设计目的:持久化存储客户端数据
- 存储大小:约 5MB(不同浏览器有差异)
- 生命周期:
- 永久存储,除非手动清除
- 需通过 JS、浏览器设置或清除缓存删除
- API:
// 存数据(自动字符串化) localStorage.setItem('key', 'value') // 取数据 const data = localStorage.getItem('key') // 删除 localStorage.removeItem('key') // 清空 localStorage.clear() - 作用域:
- 同源策略:相同协议+域名+端口共享数据
- 不同标签页/窗口共享
- 通信特性:不随请求发送
- 适用场景:长期保存的用户偏好设置、缓存数据
3. sessionStorage
- 设计目的:会话级临时存储
- 存储大小:约 5MB
- 生命周期:
- 会话结束时自动清除(关闭标签页/浏览器)
- 页面刷新保留数据
- 浏览器恢复会话时可能保留(部分浏览器)
- API:同 localStorage(
setItem/getItem/removeItem) - 作用域:
- 同源 + 同标签页(不同标签页不共享)
- 通信特性:不随请求发送
- 适用场景:表单多步骤填写、单页应用临时状态
三者的核心区别总结
| 特性 | Cookie | localStorage | sessionStorage |
|---|---|---|---|
| 设计初衷 | 服务器通信 | 持久化本地存储 | 会话级临时存储 |
| 最大容量 | 4KB | ≈5MB | ≈5MB |
| 生命周期 | 可设置过期时间 | 永久存储 | 标签页关闭即失效 |
| 自动发送至服务端 | ✔️ (每次请求) | ✘ | ✘ |
| API 易用性 | 复杂(字符串操作) | 简单(键值对) | 简单(键值对) |
| 作用域 | 跨标签页同源共享 | 跨标签页同源共享 | 仅当前标签页同源 |
| 典型应用场景 | 用户身份认证 | 主题设置/长期缓存 | 表单草稿/临时状态 |
如何选择?
- 需要与服务器通信 → 用
cookie(尤其认证信息) - 需持久化存储用户数据 → 选
localStorage - 临时保存敏感操作数据 → 选
sessionStorage(如支付流程)
安全提示:避免在 localStorage 中存储敏感信息(如密码),易受 XSS 攻击。对于高敏感数据,优先考虑服务端存储。
拓展场景题:跨页面登录表单数据传递:用户在产品页面A填写手机号表单后需跳转至登录页面B,要求自动回填手机号,实现方案?
方案对比
方案 1:URL 参数传递 + sessionStorage(安全推荐)
// 页面 A - 提交前处理
const formData = {
phone: '13800138000',
// 其他字段...
};
// 生成唯一令牌存储数据
const token = 'temp_' + Date.now();
sessionStorage.setItem(token, JSON.stringify(formData));
// 跳转时通过 URL 传递令牌
window.location.href = `login.html?token=${token}`;
// 页面 B (login.html) - 页面加载时
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
if (token && token.startsWith('temp_')) {
const formData = JSON.parse(sessionStorage.getItem(token) || '{}');
// 填充表单
document.getElementById('phone-input').value = formData.phone || '';
// 立即清除临时数据
sessionStorage.removeItem(token);
// 清除 URL 参数(不显示在地址栏)
history.replaceState({}, document.title, window.location.pathname);
}
优点:无敏感信息暴露,自动清理数据
场景:同源/跨域均可使用
方案 2:加密 URL 参数传递(无存储)
// 页面 A
import CryptoJS from 'crypto-js'; // 使用 AES 加密库
const formData = { phone: '13800138000' };
const encrypted = CryptoJS.AES.encrypt(
JSON.stringify(formData),
'YOUR_SECRET_KEY'
).toString();
// 转换为 URL-safe 格式
const encoded = encodeURIComponent(encrypted);
location.href = `login.html?data=${encoded}`;
// 页面 B
const urlParams = new URLSearchParams(window.location.search);
const encrypted = urlParams.get('data');
if (encrypted) {
const decrypted = CryptoJS.AES.decrypt(
decodeURIComponent(encrypted),
'YOUR_SECRET_KEY'
).toString(CryptoJS.enc.Utf8);
const formData = JSON.parse(decrypted || '{}');
// ...填充表单...
// 清理 URL
history.replaceState({}, document.title, window.location.pathname);
}
优点:避免服务端依赖
注意:需在前端维护密钥
方案 3:服务端中转存储
sequenceDiagram
A页面->>服务器: POST 表单数据 + 生成临时ID
服务器-->>A页面: 返回临时ID(如:temp_123)
A页面->>B页面: 跳转携带ID (login.html?tempId=123)
B页面->>服务器: 请求ID对应数据
服务器-->>B页面: 返回脱敏数据(如:138****0000)
B页面->>用户: 填充表单
B页面->>服务器: 提交后删除临时数据
优点:最安全的数据处理方式
最佳实践:
- 设置临时数据 5 分钟过期
- 返回部分脱敏信息(如隐藏中间四位)
- 使用 HTTPS 传输
关键安全考虑
-
敏感数据处理:
- 手机号需在前端脱敏显示:
138****0000 - 避免完整信息暴露在 URL 或客户端存储
- 手机号需在前端脱敏显示:
-
数据时效:
// 检查数据时效(示例) if (Date.now() - parseInt(token.split('_')[1]) > 300000) { // 超过 5 分钟则拒绝使用 } -
用户感知:
- 添加提示文案:"检测到您输入的手机号,已自动填充 ✓"
- 允许用户手动修改
方案选择建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 企业级系统 | 服务端中转 | 最高安全等级 |
| SPA 应用内跳转 | sessionStorage | 简单高效 |
| 无服务端支持 | 加密 URL 参数 | 避免明文暴露 |
| 需兼容旧浏览器 | 服务端中转 + 脱敏返回 | 避开 storage 兼容问题 |
永远不要这样:
❌location.href = "login.html?phone=13800138000"
❌ 将完整敏感数据存在 localStorage
❌ 明文写入 cookie