HTML5 本地存储面试题全解析
一、核心要点速览
💡 核心考点
- localStorage: 永久存储,~5MB 容量
- sessionStorage: 会话级存储,窗口关闭即清除
- Cookie: 传统存储,自动携带到服务器
- IndexedDB: 大容量结构化数据存储
- 使用场景: 根据需求选择合适的存储方案
二、三种存储方案详细对比
1. 特性对比表
| 特性 | localStorage | sessionStorage | Cookie |
|---|---|---|---|
| 生命周期 | 永久 | 会话级(窗口关闭) | 可设置过期时间 |
| 存储大小 | ~5MB | ~5MB | ~4KB |
| 服务器通信 | 不参与 | 不参与 | 自动携带 |
| 作用域 | 同源窗口共享 | 当前窗口 | 所有窗口 |
| API | 简单易用 | 简单易用 | 复杂 |
| 数据类型 | 字符串 | 字符串 | 字符串 |
| 安全性 | XSS 风险 | XSS 风险 | CSRF + XSS |
| 操作复杂度 | O(1) | O(1) | O(n) |
2. 使用场景决策树
需要跨会话存储?
│
├─ 是 → 需要服务器访问?
│ │
│ ├─ 是 → Cookie (HttpOnly)
│ │ └─ Token、Session ID
│ │
│ └─ 否 → localStorage
│ └─ 用户偏好设置、主题配置、购物车
│
└─ 否 → 临时数据?
│
├─ 是 → sessionStorage
│ └─ 表单临时数据、一次性消息、分页状态
│
└─ 否 → 大量结构化数据?
│
├─ 是 → IndexedDB
│ └─ 离线应用、文件缓存、数据库
│
└─ 否 → localStorage
└─ 简单键值对存储三、localStorage 详解
1. 基础 API
javascript
// 1. 存储数据
localStorage.setItem('key', 'value')
// 2. 读取数据
const value = localStorage.getItem('key')
// 3. 删除单个数据
localStorage.removeItem('key')
// 4. 清空所有数据
localStorage.clear()
// 5. 获取存储长度
const length = localStorage.length
// 6. 通过索引获取 key
const key = localStorage.key(0)2. 存储对象和数组
javascript
// ❌ 错误:直接存储对象
const user = { name: 'Vue', age: 3 }
localStorage.setItem('user', user)
// 结果:"[object Object]"
// ✓ 正确:使用 JSON 序列化
const user = { name: 'Vue', age: 3 }
localStorage.setItem('user', JSON.stringify(user))
// 读取时解析
const savedUser = JSON.parse(localStorage.getItem('user'))
console.log(savedUser.name) // 'Vue'
// ✓ 封装工具函数
const storage = {
set(key, value) {
localStorage.setItem(key, JSON.stringify(value))
},
get(key) {
const value = localStorage.getItem(key)
return value ? JSON.parse(value) : null
},
remove(key) {
localStorage.removeItem(key)
}
}
// 使用
storage.set('config', { theme: 'dark', lang: 'zh' })
const config = storage.get('config')3. 监听存储变化
javascript
// storage 事件只在其他窗口/标签页触发
window.addEventListener('storage', (e) => {
console.log(`
Key: ${e.key}
Old Value: ${e.oldValue}
New Value: ${e.newValue}
URL: ${e.url}
Storage Area: ${e.storageArea}
`)
// 应用场景:多标签页同步
if (e.key === 'theme') {
document.body.className = e.newValue
}
})
// 主动通知其他标签页
function notifyThemeChange(theme) {
localStorage.setItem('theme', theme)
// 触发 storage 事件
}四、sessionStorage 详解
1. 与 localStorage 的区别
javascript
// sessionStorage API 与 localStorage 完全相同
sessionStorage.setItem('temp', 'data')
const data = sessionStorage.getItem('temp')
sessionStorage.removeItem('temp')
// 关键区别:生命周期
// - localStorage: 永久存储,除非手动清除
// - sessionStorage: 窗口/标签页关闭即清除
// 示例:表单草稿保存
const form = document.querySelector('#myForm')
form.addEventListener('input', () => {
sessionStorage.setItem('formData', JSON.stringify({
name: form.name.value,
email: form.email.value
}))
})
// 页面加载时恢复
window.addEventListener('load', () => {
const saved = sessionStorage.getItem('formData')
if (saved) {
const data = JSON.parse(saved)
form.name.value = data.name
form.email.value = data.email
}
})
// 提交后清除
form.addEventListener('submit', (e) => {
sessionStorage.removeItem('formData')
})2. 典型应用场景
┌──────────────────────────────────────────────────────────┐
│ sessionStorage 应用场景 │
└──────────────────────────────────────────────────────────┘
✓ 表单临时数据
┌──────────────────────────────┐
│ 用户填写表单 → 临时保存 │
│ 提交成功 → 清除 │
│ 关闭窗口 → 自动清除 │
└──────────────────────────────┘
✓ 分页状态
┌──────────────────────────────┐
│ 列表页 → 保存当前页码 │
│ 进入详情 → 返回列表保持状态 │
│ 关闭窗口 → 状态清除 │
└──────────────────────────────┘
✓ 一次性消息
┌──────────────────────────────┐
│ Toast 提示 → 存储显示状态 │
│ 显示后 → 立即清除 │
└──────────────────────────────┘
✗ 不适合的场景:
- 用户偏好设置(应用 localStorage)
- 登录 token(应用 Cookie)
- 长期缓存数据
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━五、Cookie 详解
1. Cookie 操作
javascript
// 1. 设置 Cookie(不推荐手动操作,用库更好)
document.cookie = "name=value; expires=Thu, 31 Dec 2024 23:59:59 UTC; path=/; domain=.example.com; secure; HttpOnly"
// 2. 读取 Cookie
const cookies = document.cookie.split('; ').reduce((acc, cookie) => {
const [key, value] = cookie.split('=')
acc[key] = decodeURIComponent(value)
return acc
}, {})
// 3. 删除 Cookie(设置过期时间为过去)
document.cookie = "name=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"
// ✓ 推荐使用 js-cookie 库
import Cookies from 'js-cookie'
Cookies.set('name', 'value', {
expires: 7, // 7 天
path: '/', // 路径
secure: true, // 仅 HTTPS
sameSite: 'strict' // CSRF 防护
})
const value = Cookies.get('name')
Cookies.remove('name')2. Cookie 重要属性
┌──────────────────────────────────────────────────────────┐
│ Cookie 重要属性 │
└──────────────────────────────────────────────────────────┘
Secure:
┌────────────────────────────────┐
│ ✓ 仅通过 HTTPS 传输 │
│ ✗ HTTP 请求不会携带 │
│ 生产环境必须设置! │
└────────────────────────────────┘
HttpOnly:
┌────────────────────────────────┐
│ ✓ JavaScript 无法访问 │
│ ✗ 防止 XSS 窃取 Cookie │
│ 敏感信息必须设置! │
└────────────────────────────────┘
SameSite:
┌────────────────────────────────┐
│ Strict: 完全禁止第三方 Cookie │
│ Lax: 允许部分第三方请求 │
│ None: 允许所有(需 Secure) │
│ CSRF 防护的重要手段! │
└────────────────────────────────┘
Domain & Path:
┌────────────────────────────────┐
│ Domain: 指定 Cookie 作用域 │
│ Path: 指定 Cookie 路径 │
│ 合理设置避免泄露! │
└────────────────────────────────┘六、IndexedDB 简介
1. 基础用法
javascript
// 1. 打开数据库
const request = indexedDB.open('MyDatabase', 1)
// 2. 数据库升级(首次创建或版本号增加)
request.onupgradeneeded = (event) => {
const db = event.target.result
// 创建对象存储空间(类似表)
const store = db.createObjectStore('users', {
keyPath: 'id', // 主键
autoIncrement: true // 自增
})
// 创建索引
store.createIndex('name', 'name', { unique: false })
store.createIndex('email', 'email', { unique: true })
}
// 3. 使用数据库
request.onsuccess = (event) => {
const db = event.target.result
// 添加数据
function addUser() {
const tx = db.transaction('users', 'readwrite')
const store = tx.objectStore('users')
store.add({ name: 'Vue', age: 3, email: '[email protected]' })
tx.oncomplete = () => console.log('添加成功')
}
// 查询数据
function getUser(id) {
const tx = db.transaction('users', 'readonly')
const store = tx.objectStore('users')
const request = store.get(id)
request.onsuccess = () => {
console.log('用户:', request.result)
}
}
// 使用索引查询
function getUserByName(name) {
const tx = db.transaction('users', 'readonly')
const store = tx.objectStore('users')
const index = store.index('name')
const request = index.get(name)
request.onsuccess = () => {
console.log('用户:', request.result)
}
}
}
request.onerror = (event) => {
console.error('数据库错误:', event.target.error)
}2. IndexedDB 特点
┌──────────────────────────────────────────────────────────┐
│ IndexedDB 特点 │
└──────────────────────────────────────────────────────────┘
优势:
✓ 大容量:无硬限制,通常 > 50MB
✓ 结构化:支持对象存储和索引
✓ 事务:完整的事务支持
✓ 查询:支持索引查询和游标遍历
✓ 文件:可以存储 Blob/File 类型
劣势:
✗ 复杂:API 较为复杂
✗ 异步:所有操作都是异步的
✗ 兼容性:需要 polyfill(老浏览器)
适用场景:
✓ 离线应用的数据缓存
✓ 大量结构化数据存储
✓ 文件、图片存储
✓ 复杂的查询需求
不适用场景:
✗ 简单的键值对(用 localStorage)
✗ 小数据量(杀鸡用牛刀)
✗ 需要同步操作
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━七、存储方案性能对比
1. 容量对比
存储容量可视化:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cookie:
┌────────────────────────────────┐
│ ████ │ 4KB
│ 每个域名 │
│ 每次请求都发送,浪费带宽 │
└────────────────────────────────┘
localStorage/sessionStorage:
┌────────────────────────────────┐
│ ██████████████████████████████│ 5MB
│ 每个源 (origin) │
│ 不发送到服务器 │
└────────────────────────────────┘
IndexedDB:
┌────────────────────────────────┐
│ ██████████████████████████████│ 无硬限制
│ ██████████████████████████████│ 动态分配
│ ██████████████████████████████│ (通常 > 50MB)
│ 适合大数据存储 │
└────────────────────────────────┘
性能影响:
Cookie 过多 → 请求变慢
localStorage → 不影响请求
IndexedDB → 异步操作不阻塞
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━2. 读写性能测试
javascript
// 性能对比测试
const iterations = 10000
// Cookie
console.time('Cookie')
for (let i = 0; i < iterations; i++) {
document.cookie = `key${i}=value${i}`
}
console.timeEnd('Cookie') // ~500ms
// localStorage
console.time('localStorage')
for (let i = 0; i < iterations; i++) {
localStorage.setItem(`key${i}`, `value${i}`)
}
console.timeEnd('localStorage') // ~50ms
// IndexedDB (异步,实际更快)
console.time('IndexedDB')
// ... 批量写入操作
console.timeEnd('IndexedDB') // ~20ms
结论:
✓ localStorage: 比 Cookie 快约 10 倍
✓ IndexedDB: 大数据场景最优
✗ Cookie: 性能最差八、安全注意事项
1. 安全风险与防护
┌──────────────────────────────────────────────────────────┐
│ 本地存储安全最佳实践 │
└──────────────────────────────────────────────────────────┘
❌ 不要存储敏感信息:
┌────────────────────────────────┐
│ localStorage 可以被 JavaScript │
│ 访问,XSS 攻击可以轻松窃取 │
│ │
│ ✗ 密码、token、银行卡号等 │
│ 绝对不能存 localStorage! │
└────────────────────────────────┘
✓ 使用 HttpOnly Cookie:
┌────────────────────────────────┐
│ 敏感信息应该存在 HttpOnly │
│ Cookie 中,JavaScript 无法访问 │
│ │
│ ✓ Token、Session ID │
│ ✓ 用户身份标识 │
└────────────────────────────────┘
✓ 数据验证:
┌────────────────────────────────┐
│ 从存储读取的数据要验证 │
│ │
│ try {
│ const data = JSON.parse(
│ localStorage.getItem('x')
│ )
│ // 验证数据结构
│ } catch(e) {
│ // 处理错误
│ }
└────────────────────────────────┘
✓ CSP 策略:
┌────────────────────────────────┐
│ 设置 Content-Security-Policy │
│ 减少 XSS 攻击风险 │
│ │
│ Content-Security-Policy: │
│ default-src 'self' │
└────────────────────────────────┘九、面试标准回答
HTML5 提供了三种主要的本地存储方案:localStorage、sessionStorage 和 IndexedDB,另外传统的 Cookie 也还在使用。
localStorage 是永久存储,容量约 5MB,数据一直保留直到手动清除。适用于用户偏好设置、主题配置等需要跨会话的数据。但它不能存储敏感信息,因为 XSS 攻击可以窃取。
sessionStorage 与 localStorage API 相同,但生命周期仅限于当前窗口或标签页,关闭即清除。适用于表单临时数据、分页状态等一次性数据。
Cookie 容量只有 4KB,会自动发送到服务器,浪费带宽。现在主要用于身份认证(配合 HttpOnly 保证安全)。
IndexedDB 是大容量结构化存储,没有硬限制,支持事务和索引查询。适用于离线应用、大量数据缓存等复杂场景。
选择策略:
- 简单键值对 → localStorage
- 临时数据 → sessionStorage
- 敏感信息 → HttpOnly Cookie
- 大量结构化数据 → IndexedDB
十、记忆口诀
本地存储歌诀:
local 永久 session 暂,
Cookie 太小还费网。
五兆空间 local 好,
敏感信息 Cookie 藏。
indexDB 容量大,
结构化数据存储强。
XSS 要防范,
HttpOnly 保安全!十一、推荐资源
十二、总结一句话
- localStorage: 永久存储 + 大容量 = 用户偏好首选 💾
- sessionStorage: 会话级 + 窗口隔离 = 临时数据最佳 ⏱️
- Cookie: 小容量 + 自动携带 = 身份认证专用 🔐
- IndexedDB: 超大容量 + 结构化 = 离线应用利器 🗄️