一、本地存储方案对比概览
二、各方案详细介绍及示例
1. Cookie
特点:最早的前端存储方案,每次请求自动携带到服务器
// 设置Cookie
document.cookie = "username=John; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/";
// 读取Cookie
function getCookie(name) {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
const [key, value] = cookie.trim().split('=');
if (key === name) return decodeURIComponent(value);
}
return null;
}
// 封装Cookie操作类
class CookieManager {
static set(name, value, days = 7) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = `expires=${date.toUTCString()}`;
document.cookie = `${name}=${encodeURIComponent(value)};${expires};path=/`;
}
static get(name) {
return getCookie(name);
}
static delete(name) {
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`;
}
}
2. Web Storage (LocalStorage & SessionStorage)
特点:键值对存储,简单易用
// LocalStorage 示例(持久化存储)
class LocalStorageManager {
static set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (e) {
console.error('LocalStorage存储失败:', e);
return false;
}
}
static get(key) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
} catch (e) {
console.error('LocalStorage读取失败:', e);
return null;
}
}
static remove(key) {
localStorage.removeItem(key);
}
static clear() {
localStorage.clear();
}
// 获取剩余存储空间(近似值)
static getRemainingSpace() {
const testKey = 'test';
const data = '1'.repeat(1024 * 1024); // 1MB数据
try {
localStorage.setItem(testKey, data);
localStorage.removeItem(testKey);
return '充足';
} catch (e) {
return '不足';
}
}
}
// SessionStorage 示例(会话级存储)
const SessionStorageManager = {
// 存储登录状态
setLoginState(userData) {
sessionStorage.setItem('auth', JSON.stringify({
user: userData,
timestamp: Date.now(),
expiresIn: 3600000 // 1小时
}));
},
// 检查登录状态
checkLogin() {
const auth = sessionStorage.getItem('auth');
if (!auth) return null;
const { user, timestamp, expiresIn } = JSON.parse(auth);
if (Date.now() - timestamp > expiresIn) {
sessionStorage.removeItem('auth');
return null;
}
return user;
}
};
3. IndexedDB
特点:非关系型数据库,支持事务、索引,适合大量结构化数据
class IndexedDBManager {
constructor(dbName = 'AppDatabase', version = 1) {
this.dbName = dbName;
this.version = version;
this.db = null;
}
// 初始化数据库
async init(stores = [{ name: 'users', keyPath: 'id' }]) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
stores.forEach(store => {
if (!db.objectStoreNames.contains(store.name)) {
db.createObjectStore(store.name, { keyPath: store.keyPath });
}
});
};
});
}
// 添加/更新数据
async put(storeName, data) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.put(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 获取数据
async get(storeName, key) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 获取所有数据
async getAll(storeName) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 删除数据
async delete(storeName, key) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onsuccess = () => resolve(true);
request.onerror = () => reject(request.error);
});
}
}
// 使用示例
async function example() {
const db = new IndexedDBManager('MyApp', 2);
await db.init([
{ name: 'users', keyPath: 'id' },
{ name: 'products', keyPath: 'sku' }
]);
// 添加用户
await db.put('users', {
id: 1,
name: '张三',
email: 'zhangsan@example.com',
preferences: { theme: 'dark', language: 'zh-CN' }
});
// 查询用户
const user = await db.get('users', 1);
console.log(user);
}
4. Cache API
特点:用于缓存网络请求,常用于PWA和Service Worker
// Service Worker中的缓存管理
class CacheManager {
static CACHE_NAME = 'app-cache-v1';
// 缓存静态资源
static async cacheStaticAssets() {
const cache = await caches.open(this.CACHE_NAME);
return cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
]);
}
// 缓存API响应
static async cacheAPIRequest(request, response) {
const cache = await caches.open(this.CACHE_NAME);
await cache.put(request, response.clone());
}
// 获取缓存响应(网络优先)
static async getCacheFirst(request) {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
try {
const networkResponse = await fetch(request);
// 可选:缓存这个响应
if (networkResponse.ok) {
const cache = await caches.open(this.CACHE_NAME);
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
// 网络失败,返回降级内容
return new Response(JSON.stringify({
error: '网络不可用,使用缓存失败'
}), {
headers: { 'Content-Type': 'application/json' }
});
}
}
// 清理旧缓存
static async cleanOldCaches() {
const cacheNames = await caches.keys();
await Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== this.CACHE_NAME) {
return caches.delete(cacheName);
}
})
);
}
}
三、优缺点详细对比
1. Cookie
优点:
兼容性极好(所有浏览器)
自动随请求发送到服务器
可设置过期时间、域名、路径等属性
支持HTTP-only(防XSS)和Secure(仅HTTPS)标记
缺点:
容量小(4KB)
每次请求都携带,增加流量消耗
安全性问题(可能被CSRF攻击)
API简陋,操作不便
同步操作可能阻塞页面
2. LocalStorage
优点:
容量较大(5-10MB)
操作简单,API友好
同源页面共享
持久化存储,手动清理
缺点:
仅存储字符串,需要JSON转换
同步操作,大量数据可能阻塞页面
无法在Web Worker中访问
无数据过期机制
安全性较低(易受XSS攻击)
3. SessionStorage
优点:
会话隔离,隐私性好
页面刷新不影响数据
操作简单,API友好
缺点:
容量限制(5-10MB)
标签页关闭数据丢失
同步操作
无法跨标签页共享
4. IndexedDB
优点:
容量大(通常250MB以上)
支持事务,保证数据一致性
异步操作,不阻塞页面
支持复杂查询和索引
可在Web Worker中使用
缺点:
API复杂,学习曲线陡峭
兼容性相对较差(但现代浏览器都支持)
需要手动管理数据库版本
调试相对困难
5. Cache API
优点:
专门为网络请求缓存设计
支持请求/响应对象存储
Service Worker集成
离线访问支持
缺点:
主要用于缓存网络资源
不适合存储应用状态数据
Service Worker要求HTTPS
管理相对复杂
四、综合总结与建议
选择策略
最佳实践示例
1. 安全存储策略
// 封装安全的存储管理器
class SecureStorage {
constructor(storage = localStorage) {
this.storage = storage;
}
// 加密存储(简单示例,生产环境使用更强大的加密)
set(key, value, encrypt = false) {
try {
const data = encrypt ? this.encrypt(value) : value;
this.storage.setItem(key, JSON.stringify({
data,
encrypted: encrypt,
timestamp: Date.now()
}));
} catch (e) {
console.error('存储失败:', e);
}
}
get(key) {
try {
const item = JSON.parse(this.storage.getItem(key));
if (!item) return null;
return item.encrypted ? this.decrypt(item.data) : item.data;
} catch (e) {
console.error('读取失败:', e);
return null;
}
}
encrypt(text) {
// 实际项目应使用更安全的加密算法
return btoa(encodeURIComponent(text));
}
decrypt(text) {
return decodeURIComponent(atob(text));
}
}
2. 存储容量管理
// 存储容量监控和管理
class StorageMonitor {
static checkLocalStorageQuota() {
try {
// 测试存储容量
const testData = '1'.repeat(1024 * 1024); // 1MB
localStorage.setItem('__test__', testData);
localStorage.removeItem('__test__');
return '空间充足';
} catch (e) {
// 清理旧数据
this.cleanupOldData();
return '空间不足,已清理旧数据';
}
}
static cleanupOldData() {
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
const keys = Object.keys(localStorage);
keys.forEach(key => {
try {
const item = JSON.parse(localStorage.getItem(key));
if (item && item.timestamp && item.timestamp < oneWeekAgo) {
localStorage.removeItem(key);
}
} catch (e) {
// 非JSON数据,根据需求处理
}
});
}
}
3. 混合存储方案示例
// 根据数据类型智能选择存储方案
class HybridStorage {
constructor() {
this.schemes = {
cookie: { maxSize: 4 * 1024 },
localStorage: { maxSize: 5 * 1024 * 1024 },
indexedDB: { maxSize: Infinity }
};
}
async store(key, value, options = {}) {
const size = this.getSize(value);
if (size <= this.schemes.cookie.maxSize && options.sendToServer) {
// 小数据且需要服务器访问 -> Cookie
CookieManager.set(key, value, options.expires);
} else if (size <= this.schemes.localStorage.maxSize) {
// 中等数据 -> LocalStorage
LocalStorageManager.set(key, value);
} else {
// 大数据 -> IndexedDB
const db = new IndexedDBManager();
await db.init();
await db.put('hybridData', { key, value, timestamp: Date.now() });
}
}
getSize(obj) {
return new Blob([JSON.stringify(obj)]).size;
}
}
建议
-
简单应用:优先使用LocalStorage,注意数据大小和安全性
-
复杂应用:使用IndexedDB存储结构化数据,LocalStorage存储配置信息
-
需要服务器通信:使用Cookie存储会话标识等小数据
-
PWA/离线应用:Cache API + IndexedDB组合
-
敏感数据:避免在前端存储,必须存储时进行加密
-
定期清理:实现数据过期和清理机制
-
错误处理:所有存储操作都要有try-catch
-
降级策略:考虑存储失败时的降级方案
根据具体项目需求选择合适的存储方案,多数情况下组合使用多种方案能达到最佳效果。