Cookie是最传统的客户端存储方式,主要用于会话管理和个性化设置。
// 设置Cookie
document.cookie = "username=John; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/";
document.cookie = "theme=dark; max-age=2592000; secure; samesite=strict";
// 读取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
function deleteCookie(name) {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
}
// 基本操作
localStorage.setItem('user', JSON.stringify({ name: 'Alice', age: 25 }));
const user = JSON.parse(localStorage.getItem('user'));
localStorage.removeItem('user');
localStorage.clear();
// 封装实用类
class LocalStorageManager {
constructor() {
this.storage = window.localStorage;
}
set(key, value) {
this.storage.setItem(key, JSON.stringify(value));
return this;
}
get(key) {
const item = this.storage.getItem(key);
return item ? JSON.parse(item) : null;
}
remove(key) {
this.storage.removeItem(key);
}
clear() {
this.storage.clear();
}
// 监听存储变化
static addChangeListener(callback) {
window.addEventListener('storage', (e) => {
if (e.storageArea === localStorage) {
callback(e);
}
});
}
}
// 使用示例
const storage = new LocalStorageManager();
storage.set('settings', { theme: 'dark', notifications: true });
// 使用方法与localStorage相同,但生命周期不同
sessionStorage.setItem('sessionId', 'abc123xyz');
const sessionId = sessionStorage.getItem('sessionId');
// 标签页关闭时自动清除
| 特性 | LocalStorage | SessionStorage | Cookie |
|---|---|---|---|
| 容量 | 5-10MB | 5-10MB | 4KB |
| 生命周期 | 永久(除非手动清除) | 标签页会话期间 | 可设置过期时间 |
| 自动发送到服务器 | 否 | 否 | 是 |
| 访问范围 | 同源所有标签页 | 当前标签页 | 同源所有标签页 |
IndexedDB是浏览器内置的NoSQL数据库,适合存储大量结构化数据。
// 数据库操作类
class IndexedDBManager {
constructor(dbName, version = 1) {
this.dbName = dbName;
this.version = version;
this.db = null;
}
// 打开或创建数据库
open(upgradeCallback) {
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) => {
this.db = event.target.result;
if (upgradeCallback) upgradeCallback(this.db, event);
};
});
}
// 创建对象仓库(表)
createObjectStore(storeName, options = { keyPath: 'id', autoIncrement: true }) {
if (!this.db) throw new Error('Database not opened');
return this.db.createObjectStore(storeName, options);
}
// 添加索引
createIndex(storeName, indexName, keyPath, options = { unique: false }) {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
return store.createIndex(indexName, keyPath, options);
}
// 添加数据
add(storeName, data) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.add(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 获取数据
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);
});
}
// 获取所有数据
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);
});
}
// 使用索引查询
getByIndex(storeName, indexName, value) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const index = store.index(indexName);
const request = index.getAll(value);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 更新数据
update(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);
});
}
// 删除数据
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(request.result);
request.onerror = () => reject(request.error);
});
}
// 清空对象仓库
clear(storeName) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
// 删除数据库
static deleteDatabase(dbName) {
return new Promise((resolve, reject) => {
const request = indexedDB.deleteDatabase(dbName);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}
// 使用示例
async function setupUserDatabase() {
const dbManager = new IndexedDBManager('UserDB', 2);
await dbManager.open((db, event) => {
// 版本1:创建基础用户表
if (!db.objectStoreNames.contains('users')) {
const store = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true
});
// 创建索引
store.createIndex('email', 'email', { unique: true });
store.createIndex('age', 'age', { unique: false });
store.createIndex('name', 'name', { unique: false });
}
// 版本2:添加用户设置表
if (event.oldVersion < 2) {
db.createObjectStore('user_settings', { keyPath: 'userId' });
}
});
return dbManager;
}
// 使用数据库
async function main() {
try {
const db = await setupUserDatabase();
// 添加用户
await db.add('users', {
name: 'John Doe',
email: 'john@example.com',
age: 30,
createdAt: new Date()
});
// 查询用户
const users = await db.getAll('users');
console.log('所有用户:', users);
// 通过邮箱查询
const userByEmail = await db.getByIndex('users', 'email', 'john@example.com');
console.log('通过邮箱查询:', userByEmail);
// 更新用户
const userToUpdate = users[0];
userToUpdate.age = 31;
await db.update('users', userToUpdate);
} catch (error) {
console.error('数据库操作失败:', error);
}
}
// 事务批处理示例
async function batchOperations(db) {
const transaction = db.db.transaction(['users', 'user_settings'], 'readwrite');
const userStore = transaction.objectStore('users');
const settingsStore = transaction.objectStore('user_settings');
// 添加用户
const userRequest = userStore.add({
name: 'Alice Smith',
email: 'alice@example.com',
age: 28
});
userRequest.onsuccess = (event) => {
const userId = event.target.result;
// 为用户添加设置
settingsStore.add({
userId: userId,
theme: 'dark',
notifications: true,
language: 'en'
});
};
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
| 需求场景 | 推荐方案 | 原因 |
|---|---|---|
| 会话管理、身份验证 | Cookie | 自动发送到服务器,支持HttpOnly安全标记 |
| 用户偏好设置 | LocalStorage | 容量适中,持久化存储 |
| 单标签页临时数据 | SessionStorage | 标签页关闭自动清理 |
| 少量简单数据 | Web Storage | API简单,同步操作 |
| 大量结构化数据 | IndexedDB | 容量大,支持索引查询 |
| 离线应用数据 | IndexedDB + Service Worker | 支持离线操作,容量大 |
| 二进制数据存储 | IndexedDB | 支持Blob、ArrayBuffer |
| 存储方式 | 典型容量 | 实际限制 |
|---|---|---|
| Cookie | 4KB | 每个域名约50个cookie |
| LocalStorage | 5-10MB | 不同浏览器有差异 |
| SessionStorage | 5-10MB | 同LocalStorage |
| IndexedDB | 50%硬盘空间(桌面) | 通常至少1GB,可请求更多 |
// 安全存储示例
class SecureStorage {
constructor() {
this.encoder = new TextEncoder();
this.decoder = new TextDecoder();
}
// 简单混淆(注意:不是加密)
async storeSensitiveData(key, data) {
const jsonStr = JSON.stringify(data);
const encoded = btoa(unescape(encodeURIComponent(jsonStr)));
// 在实际应用中,应考虑使用Web Crypto API进行加密
localStorage.setItem(key, encoded);
}
async retrieveSensitiveData(key) {
const encoded = localStorage.getItem(key);
if (!encoded) return null;
try {
const jsonStr = decodeURIComponent(escape(atob(encoded)));
return JSON.parse(jsonStr);
} catch (error) {
console.error('数据解析失败:', error);
return null;
}
}
// 真正的加密存储(需要HTTPS)
async encryptAndStore(key, data, password) {
// 使用Web Crypto API进行加密
// 这里仅展示概念,实际实现更复杂
console.warn('请使用Web Crypto API实现真正的加密');
}
}
// IndexedDB性能优化技巧
class OptimizedIndexedDB extends IndexedDBManager {
// 批量操作
async bulkAdd(storeName, items) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
let completed = 0;
items.forEach((item, index) => {
const request = store.add(item);
request.onsuccess = () => {
completed++;
if (completed === items.length) {
resolve();
}
};
request.onerror = () => reject(request.error);
});
});
}
// 分页查询
async getPaginated(storeName, page = 1, pageSize = 20) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const index = store.index('createdAt'); // 假设有创建时间索引
const offset = (page - 1) * pageSize;
const results = [];
let count = 0;
const cursorRequest = index.openCursor(null, 'prev'); // 按时间倒序
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor && count < pageSize) {
if (results.length >= offset) {
results.push(cursor.value);
count++;
}
cursor.continue();
} else {
resolve({
page,
pageSize,
total: results.length,
data: results
});
}
};
cursorRequest.onerror = () => reject(cursorRequest.error);
});
}
}
// 存储方案降级策略
class StorageManager {
constructor() {
this.storageStrategies = [
this.tryIndexedDB.bind(this),
this.tryLocalStorage.bind(this),
this.tryCookie.bind(this)
];
}
async setItem(key, value) {
for (const strategy of this.storageStrategies) {
try {
await strategy.setItem(key, value);
return { success: true, method: strategy.name };
} catch (error) {
console.warn(`${strategy.name} 失败:`, error);
continue;
}
}
throw new Error('所有存储方案都失败了');
}
async tryIndexedDB() {
// IndexedDB实现
const db = new IndexedDBManager('fallbackDB');
await db.open();
// ... 具体实现
}
tryLocalStorage() {
// LocalStorage实现
if (!window.localStorage) throw new Error('LocalStorage不可用');
localStorage.setItem(key, JSON.stringify(value));
}
tryCookie() {
// Cookie实现
document.cookie = `${key}=${encodeURIComponent(JSON.stringify(value))}; max-age=31536000`;
}
}
// 缓存API示例
async function cacheResources() {
const cacheName = 'my-app-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
];
try {
const cache = await caches.open(cacheName);
await cache.addAll(urlsToCache);
console.log('资源已缓存');
} catch (error) {
console.error('缓存失败:', error);
}
}
// 从缓存获取
async function getCachedResponse(request) {
const cache = await caches.open('my-app-cache-v1');
const cachedResponse = await cache.match(request);
if (cachedResponse) {
return cachedResponse;
}
// 如果缓存中没有,则请求网络
const networkResponse = await fetch(request);
// 可选:将响应添加到缓存
if (networkResponse.ok) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
}
选择合适的存储方案需要考虑以下因素:
数据大小:小数据用Cookie/Web Storage,大数据用IndexedDB 持久性需求:临时数据用SessionStorage,永久数据用LocalStorage/IndexedDB 结构化程度:简单键值对用Web Storage,复杂数据用IndexedDB 离线需求:IndexedDB + Cache API 安全性要求:敏感数据用HttpOnly Cookie或加密存储在实际开发中,通常需要组合使用多种存储方案,以平衡性能、容量和功能需求。IndexedDB虽然API较复杂,但对于现代Web应用来说是功能最强大、最灵活的客户端存储方案。