本文 AI 產出,尚未審核

JavaScript 課程 — DOM 與瀏覽器 API

主題:LocalStorage / SessionStorage


簡介

在前端開發中,我們常需要在 使用者離開頁面 後仍能保留一些狀態或資料,例如使用者的偏好設定、表單暫存或是購物車內容。傳統上只能透過伺服器端的 Session 或 Cookie 來達成,但這兩者都有容量限制、需額外傳輸或安全性考量。
HTML5 為瀏覽器提供了兩個 本地儲存 (Web Storage) 機制——localStoragesessionStorage,讓開發者可以直接在客戶端以 鍵值對 (key‑value) 形式 存取資料,且不需要每一次請求都把資料回傳給伺服器。

本篇文章將深入說明這兩個 API 的差異、使用方式與實務應用,幫助初學者快速上手,同時提供中階開發者在大型專案中安全、有效地運用本地儲存的技巧。


核心概念

1. LocalStorage 與 SessionStorage 的共同點

特性 localStorage sessionStorage
儲存期限 永久(除非主動刪除或使用者清除瀏覽器資料) 只在當前瀏覽器分頁(或視窗)有效,關閉分頁即失效
容量上限 約 5 MB(依瀏覽器而略有差異) 約 5 MB(與 localStorage 相同)
作用範圍 同源(protocol、host、port)下的所有分頁共享 同源且同一分頁(或同一視窗)專屬
資料類型 只能存字串,其他類型需自行序列化 同上

重點:兩者都只能存 字串,若要儲存物件或陣列,需要先使用 JSON.stringify() 轉成字串,取出時再用 JSON.parse() 轉回原型。

2. 基本操作 API

// 設定資料
localStorage.setItem('theme', 'dark');     // 儲存字串
sessionStorage.setItem('token', 'abc123'); // 儲存字串

// 取得資料
const theme = localStorage.getItem('theme');   // => 'dark'
const token = sessionStorage.getItem('token'); // => 'abc123'

// 移除單筆資料
localStorage.removeItem('theme');

// 清除全部資料(僅限於該 storage)
sessionStorage.clear();
  • setItem(key, value):寫入或覆蓋指定 key 的值。
  • getItem(key):取得指定 key 的值,若不存在回傳 null
  • removeItem(key):刪除指定 key。
  • clear():一次清除所有資料(只會影響當前 storage)。

3. 事件監聽 – storage 事件

同源的不同分頁localStorage 作出變更時,其他分頁會收到 storage 事件,可用於即時同步 UI。

window.addEventListener('storage', (event) => {
  console.log('Key changed:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('Storage area:', event.storageArea === localStorage ? 'localStorage' : 'sessionStorage');
});

注意sessionStorage 不會觸發 storage 事件,因為它僅在同一分頁中有效。

4. 範例:儲存使用者偏好設定

以下示範如何在使用者切換「深色/淺色」主題時,把選擇寫入 localStorage,並在頁面載入時自動套用。

// 1. 讀取先前的設定(若無則使用預設值)
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.dataset.theme = savedTheme;

// 2. 切換主題的函式
function toggleTheme() {
  const current = document.documentElement.dataset.theme;
  const next = current === 'light' ? 'dark' : 'light';
  document.documentElement.dataset.theme = next;
  // 3. 把新主題寫回 localStorage
  localStorage.setItem('theme', next);
}

// 4. 監聽按鈕點擊
document.getElementById('themeBtn').addEventListener('click', toggleTheme);

小技巧:使用 data- 屬性或 CSS 變數配合 [data-theme="dark"] 來切換樣式,可讓主題切換更簡潔。

5. 範例:表單暫存(SessionStorage)

在多人填寫長表單時,使用者若不小心關閉分頁,資料會全部遺失。利用 sessionStorage 可在 同一次瀏覽會話 內自動保存使用者的輸入。

const form = document.querySelector('#contactForm');
const FIELDS = ['name', 'email', 'message'];

// 把每次輸入即時寫入 sessionStorage
FIELDS.forEach((field) => {
  const input = form.elements[field];
  // 讀取暫存值
  const saved = sessionStorage.getItem(`form_${field}`);
  if (saved) input.value = saved;

  // 寫入暫存
  input.addEventListener('input', () => {
    sessionStorage.setItem(`form_${field}`, input.value);
  });
});

// 表單送出時,清除暫存(避免下次開啟仍留下舊資料)
form.addEventListener('submit', (e) => {
  e.preventDefault(); // 先阻止實際送出,示範用
  FIELDS.forEach((field) => sessionStorage.removeItem(`form_${field}`));
  alert('表單已送出,暫存已清除!');
});

6. 範例:購物車資料(LocalStorage + JSON)

購物車通常需要儲存 陣列或物件,以下示範把商品資訊以 JSON 字串存入 localStorage,並在頁面載入時還原。

const CART_KEY = 'shoppingCart';

// 取得購物車(若無則回傳空陣列)
function getCart() {
  const raw = localStorage.getItem(CART_KEY);
  return raw ? JSON.parse(raw) : [];
}

// 儲存購物車
function setCart(cart) {
  localStorage.setItem(CART_KEY, JSON.stringify(cart));
}

// 加入商品
function addToCart(product) {
  const cart = getCart();
  const existing = cart.find(item => item.id === product.id);
  if (existing) {
    existing.qty += product.qty;
  } else {
    cart.push(product);
  }
  setCart(cart);
}

// 刪除商品
function removeFromCart(productId) {
  const cart = getCart().filter(item => item.id !== productId);
  setCart(cart);
}

// 示範:加入一筆商品
addToCart({ id: 101, name: 'JavaScript 書籍', price: 350, qty: 1 });
console.log('目前購物車:', getCart());

常見陷阱與最佳實踐

陷阱 可能的問題 建議的解決方案
只能存字串 直接存物件會得到 [object Object],取回後無法使用 使用 JSON.stringify() / JSON.parse(),或自行實作序列化(如 Date 需特別處理)
容量超限 超過約 5 MB 時會拋出 QuotaExceededError,導致程式中斷 在寫入前檢查 storage.length 或使用 try/catch 包住 setItem,必要時清除不必要的舊資料
同步問題 在同一分頁內多個腳本同時寫入同一鍵值,可能互相覆蓋 采用 命名空間(如 app:user:profile)或使用 鎖定機制(例如 localStorage.setItem('lock', Date.now())
安全性 敏感資料(如 JWT、密碼)直接寫入會被瀏覽器開發者工具輕易查看 絕對不要 把機密資訊放入 Web Storage;改用 HttpOnly Cookie 或在伺服器端保存
跨瀏覽器/隱私模式 某些瀏覽器的隱私模式會禁用 Storage,導致 null 或例外 在使用前先檢測 typeof localStorage !== 'undefined',並提供 fallback(例如記憶體暫存)
資料過期 localStorage 永久保存,舊資料可能過時 手動加入 時間戳記,在讀取時檢查是否已過期,或使用 sessionStorage 代替短期需求

最佳實踐

  1. 統一封裝:建立一個小型的 Storage Helper,集中處理序列化、錯誤捕捉與過期檢查。
  2. 命名空間:使用類似 myApp:module:key 的格式,避免與第三方程式衝突。
  3. 容量監控storage.length 只能得知鍵的數量,若需更精確的位元組大小,可自行估算字串長度。
  4. 避免頻繁寫入setItem 會觸發 I/O,對性能有影響;可在表單暫存時使用 debounce(防抖)技巧減少寫入次數。

實際應用場景

場景 為何適合使用 Web Storage 範例實作
使用者主題偏好 只需在不同分頁間共享且長期保存 前文的主題切換範例
表單暫存 使用者可能在填寫過程中意外關閉頁面 前文的 sessionStorage 暫存範例
離線 Web App 需要在沒有網路時仍能讀取資料 把 API 回傳的 JSON 結果緩存於 localStorage,離線時直接讀取
購物車 / 心願清單 資料結構較複雜且需跨分頁持久化 前文的購物車 JSON 範例
A/B 測試或功能旗標 快速切換功能開關、收集使用者分組 把測試組別寫入 localStorage,在程式碼中根據鍵值決定 UI 行為
即時協作狀態(同源多分頁) 多分頁同時編輯同一筆資料時,需要即時同步 使用 storage 事件監聽,將變更即時反映到其他分頁

總結

  • localStoragesessionStorageHTML5 Web Storage 的兩大核心 API,分別適用於 永久保存會話暫存 的需求。
  • 兩者皆以 字串 為儲存單位,必須透過 JSON.stringify / JSON.parse 處理物件或陣列。
  • 正確使用 命名空間、錯誤捕捉與容量管理,可以避免常見的儲存失敗與資料衝突。
  • 透過 storage 事件,可實現 跨分頁即時同步,對於需要多視窗協同的應用非常有幫助。
  • 安全性 是不可忽視的議題,切勿把敏感資訊直接寫入本地儲存,應以 HttpOnly Cookie 或伺服器端驗證取代。

掌握了這套本地儲存機制後,你就能在前端開發中 更靈活地管理使用者狀態、提升體驗,同時減少不必要的伺服器請求。希望本篇文章能夠幫助你在實作中快速上手,並在日後的專案裡運用得得心應手。祝開發順利!