做前端久了,儲存方案沒想清楚踩了不少坑。系統梳理一下。
各方案對比
| | Cookie | sessionStorage | localStorage | IndexedDB | | ----------- | ---------------- | -------------- | ------------ | --------- | | 容量 | 4KB | 5-10MB | 5-10MB | >250MB | | 生命週期 | 設定過期時間 | 標籤頁關閉 | 永久 | 永久 | | 服務端訪問 | ✅(隨請求傳送) | ❌ | ❌ | ❌ | | API | 字串操作 | 同步 | 同步 | 非同步 | | 跨 Tab 共享 | ✅ | ❌ | ✅ | ✅ |
Cookie
javascript
// 讀寫 Cookie(原生 API 很難用)
document.cookie = "token=abc123; max-age=86400; path=/; SameSite=Strict";
// 推薦用 js-cookie 庫
import Cookies from "js-cookie";
Cookies.set("token", "abc123", { expires: 1 }); // 1天后過期
Cookies.get("token");
Cookies.remove("token");
安全屬性:
HttpOnly:JS 無法讀取(只能後端設定),防 XSS 偷 tokenSecure:只在 HTTPS 傳輸SameSite=Strict/Lax:防 CSRF
localStorage
javascript
// 只能存字串,物件需要序列化
localStorage.setItem("user", JSON.stringify({ name: "Alice", role: "admin" }));
const user = JSON.parse(localStorage.getItem("user"));
localStorage.removeItem("user");
localStorage.clear();
// 封裝一個帶過期時間的 storage
class ExpirableStorage {
set(key, value, ttl) {
const item = {
value,
expiry: ttl ? Date.now() + ttl : null,
};
localStorage.setItem(key, JSON.stringify(item));
}
get(key) {
const itemStr = localStorage.getItem(key);
if (!itemStr) return null;
const item = JSON.parse(itemStr);
if (item.expiry && Date.now() > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value;
}
}
IndexedDB
用於儲存大量結構化資料(快取 API 響應、離線資料等)。
javascript
// 原生 API 基於事件,比較繁瑣,推薦用 idb 庫
import { openDB } from "idb";
const db = await openDB("my-app", 1, {
upgrade(db) {
const store = db.createObjectStore("cache", { keyPath: "key" });
store.createIndex("expiry", "expiry");
},
});
// 寫入
await db.put("cache", {
key: "user-list",
data: users,
expiry: Date.now() + 300000,
});
// 讀取
const cached = await db.get("cache", "user-list");
if (cached && cached.expiry > Date.now()) {
return cached.data;
}
// Service Worker 快取 API 響應(配合使用)
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
}),
);
});
實際專案中的選擇
javascript
// Token 儲存:兩種流派
// 1. Cookie:HttpOnly,防 XSS
// 2. localStorage:方便,但 XSS 可能洩露
// 推薦:敏感 token 用 HttpOnly Cookie
// 非敏感配置(主題、語言偏好)用 localStorage
// 使用者配置
localStorage.setItem("theme", "dark");
localStorage.setItem("locale", "zh-CN");
// API 快取(考慮用 IndexedDB)
const cache = new Map(); // 記憶體快取(重新整理丟失)
// 或者用 IndexedDB 持久化快取
// 表單草稿(防止誤刷丟失)
const DRAFT_KEY = `form-draft-${formId}`;
localStorage.setItem(DRAFT_KEY, JSON.stringify(formData));
小結
- Cookie:適合需要服務端讀取的資料(token),加 HttpOnly 防 XSS
- localStorage:簡單的持久化,注意不要存敏感資訊
- sessionStorage:臨時資料,標籤頁關閉自動清除
- IndexedDB:大量結構化資料、離線快取,用 idb 簡化 API