Skip to content
⚠️ This article was written in 2019. Some content may be outdated.

瀏覽器儲存方案對比:Cookie、localStorage、IndexedDB

做前端久了,儲存方案沒想清楚踩了不少坑。系統梳理一下。

各方案對比

| | Cookie | sessionStorage | localStorage | IndexedDB | | ----------- | ---------------- | -------------- | ------------ | --------- | | 容量 | 4KB | 5-10MB | 5-10MB | >250MB | | 生命週期 | 設定過期時間 | 標籤頁關閉 | 永久 | 永久 | | 服務端訪問 | ✅(隨請求傳送) | ❌ | ❌ | ❌ | | API | 字串操作 | 同步 | 同步 | 非同步 | | 跨 Tab 共享 | ✅ | ❌ | ✅ | ✅ |

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 偷 token
  • Secure:只在 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

MIT Licensed