在前端專案開發中,如何在瀏覽器端妥善做前端資料儲存與狀態管理,經常會面臨 Cookie、localStorage 與 sessionStorage 三種機制的選擇。這三者在容量上限、存活週期、跨分頁作用域以及是否隨 HTTP 請求自動傳送給伺服器上都有本質差異。若想單獨查看 localStorage 的基礎存取範例,可先參考 localStorage 用法;而涉及跨網域限制與 Cookie 安全防禦的部分,則可搭配 瀏覽器同源策略 Same-origin policy 說明 與 HTTPOnly Cookie 怎麼設定?新手工程師常見 XSS 防禦實作 一起對照。
一、Cookie、localStorage 與 sessionStorage 規格總覽
在進入各別語法實作前,先透過下方對照表快速釐清三者在容量、生命週期、作用域與伺服器互動上的核心差異,避免在架構設計時產生非預期的效能瓶頸或資安風險:
| 比較項目 | Cookie | localStorage | sessionStorage |
|---|---|---|---|
| 儲存容量 | 約 4KB(單一網域也有數量上限) | 約 5MB(依瀏覽器實作而異) | 約 5MB(依瀏覽器實作而異) |
| 伺服器傳輸 | 每次同網域 HTTP 請求皆自動夾帶於 Header | 僅存於瀏覽器端,不隨請求發送 | 僅存於瀏覽器端,不隨請求發送 |
| 生命週期 | 依 Expires / Max-Age 決定;未設則為 Session Cookie | 永久保留,直到程式碼或使用者手動清除 | 僅限當前分頁,關閉該分頁即自動清空 |
| 作用域(共享範圍) | 依 Domain 與 Path 設定,可跨同源分頁與指定子網域共享 | 嚴格遵循同源政策(Origin),同源的多個分頁與視窗可共享 | 嚴格遵循同源政策 + 單一分頁隔離(不同分頁間不共享) |
二、三大前端儲存機制的運作原理與程式碼實作
1. Cookie
Cookie 是瀏覽器用來儲存小型鍵值對(Key-Value)文字資料的機制,常用於記錄使用者的登入工作階段(Session ID)、語系偏好或追蹤識別碼。
它的核心特性在於與伺服器雙向溝通:當瀏覽器向符合 Domain 與 Path 條件的伺服器發起 HTTP 請求時,會自動將對應的 Cookie 夾帶在 Cookie 請求標頭(Request Header)中送出。
在前端 JavaScript 中,可以透過 document.cookie 讀寫非機密的 Cookie,並設定 max-age、expires、path、Secure 與 SameSite 等屬性。但需特別注意:關鍵的防 XSS 屬性 HttpOnly 僅能由後端伺服器透過 HTTP 回應標頭(Set-Cookie)設定,前端 document.cookie 無法寫入也無法讀取帶有 HttpOnly 的 Cookie(關於未設定過期時間時的預設行為,可延伸閱讀 Cookie Expires 顯示為 Session 是什麼意思?原因與設定教學)。
// 1. 前端設定一般 Cookie,包含有效期限(7 天 = 604800 秒)與安全傳輸屬性
// 注意:HttpOnly 屬性必須由後端 Set-Cookie 回應標頭設定,不可由 document.cookie 寫入
document.cookie = "theme=dark; max-age=604800; path=/; Secure; SameSite=Strict";
// 2. 讀取目前前端可存取的 Cookie 字串(不含 HttpOnly Cookie)
const allCookies = document.cookie;
console.log(allCookies);
// 3. 刪除 Cookie(將 max-age 設為 0 或將 expires 設為過去時間,且 path 需一致)
document.cookie = "theme=; max-age=0; path=/";
2. localStorage
localStorage 屬於瀏覽器提供的 Web Storage API,允許開發者將資料以「字串型別的鍵值對」長期保存在使用者的瀏覽器中。與 Cookie 最大的不同在於,localStorage 的資料完全不會隨著 HTTP 請求發送給伺服器,且即使關閉瀏覽器分頁、關閉整個瀏覽器視窗或重新開機,資料依然會保留,直到透過程式呼叫清除或使用者手動清除瀏覽器網站資料為止。
在 JavaScript 中,主要使用 localStorage.setItem()、localStorage.getItem()、localStorage.removeItem() 以及 localStorage.clear() 進行管理。由於 Web Storage 僅能儲存字串,存取物件或陣列時須搭配 JSON.stringify() 與 JSON.parse(),並做好空值與例外防呆:
// 1. 儲存物件資料(需先透過 JSON.stringify 序列化為字串)
const userSettings = { theme: "dark", notifications: true };
localStorage.setItem("userPreferences", JSON.stringify(userSettings));
// 2. 讀取資料並轉回物件(初次造訪可能為 null,需提供預設值並搭配 try...catch 防呆)
let savedSettings = { theme: "light", notifications: false };
try {
const raw = localStorage.getItem("userPreferences");
if (raw) {
savedSettings = JSON.parse(raw);
}
} catch (err) {
console.warn("解析 localStorage JSON 失敗:", err);
}
console.log(savedSettings.theme); // 輸出: dark
// 3. 移除特定鍵值的資料
localStorage.removeItem("userPreferences");
// 4. 清空當前同源網域下的所有 localStorage 資料
localStorage.clear();
3. sessionStorage
sessionStorage 同樣屬於 Web Storage API,其操作方法與 localStorage 完全一致,但兩者的生命週期與分頁隔離機制截然不同。
sessionStorage 的資料僅在當前單一瀏覽器分頁(Top-level Browsing Context)的存活期間有效:
- 在同一個分頁內重新整理(F5)或同源頁面跳轉時,資料會持續保留。
- 一旦使用者關閉該分頁,資料就會立即被瀏覽器清空。
- 即使使用者在同一瀏覽器中再開一個相同網址的新分頁,兩個分頁之間的
sessionStorage也是各自獨立、互不共享的。
// 1. 儲存單一分頁內的表單暫存資料
sessionStorage.setItem("draftFormContent", "這是一段尚未送出的表單草稿");
// 2. 讀取暫存資料
const currentDraft = sessionStorage.getItem("draftFormContent");
console.log(currentDraft);
// 3. 表單成功送出後,移除該筆暫存鍵值
sessionStorage.removeItem("draftFormContent");
三、實際開發中的典型應用場景
了解三者的底層特性後,在實際專案架構中可依以下原則選擇適合的儲存位置:
- Cookie 的適用場景:
- 身分驗證與工作階段管理(Authentication & Session):由後端寫入加上
HttpOnly; Secure; SameSite的 Session ID 或 Refresh Token,讓瀏覽器在每次 API 請求時自動帶上憑證,同時防範前端 XSS 腳本竊取。 - 伺服器端渲染(SSR)需預先讀取的偏好狀態:例如 Next.js / Nuxt 等 SSR 架構需要在伺服器端收到第一次 HTML 請求時就知道使用者的語系(Locale)或深淺色主題,避免畫面閃爍(FOUC)。
- 身分驗證與工作階段管理(Authentication & Session):由後端寫入加上
- localStorage 的適用場景:
- 跨分頁持久化介面設定(UI Preferences):保存無涉機密的個人化設定,例如深色模式開關、側邊欄收合狀態、表格每頁顯示筆數等。
- 非敏感靜態資料快取與離線草稿:快取變動頻率低的字典檔、選單設定或長篇編輯器自動備份草稿,減少重複 API 請求。
- sessionStorage 的適用場景:
- 多步驟表單或精靈流程(Multi-step Wizard):在分步驟填寫的結帳或註冊流程中暫存各步驟欄位值,重新整理時不會遺失,且關閉分頁即自動銷毀,不會殘留過期髒資料。
- 單一分頁的列表篩選與捲動位置狀態:記錄使用者在某個分頁內的搜尋條件與閱讀位置,且不干擾使用者同時開啟的另一個分頁。
四、實務踩坑、注意事項與排查重點
在實際專案開發中,若將所有資料一律塞進 localStorage 或不當塞入 Cookie,極易引發資安漏洞或效能瓶頸。以下整理五大常見盲點與排查步驟:
1. 敏感機密資料誤存於 localStorage 的資安風險
- 問題原因:
localStorage與sessionStorage內的資料可被同源頁面上的任何 JavaScript 直接讀取。若將密碼、個人隱私個資或高權限的長期存取憑證(Token)明文存入localStorage,一旦網站存在跨站腳本攻擊(XSS)漏洞(或載入遭汙染的第三方套件),攻擊者僅需一行localStorage.getItem()即可將憑證盜走。 - 解法與檢查步驟:
- 原則:絕不將高敏感個資或長期認證憑證直接存放在
localStorage/sessionStorage。 - 防禦寫法:身分驗證憑證應交由後端透過
Set-Cookie標頭設定,並強制加上HttpOnly(禁止 JS 讀取)、Secure(僅限 HTTPS 傳輸)與SameSite=Strict或Lax(防範 CSRF 跨站請求偽造)。
- 原則:絕不將高敏感個資或長期認證憑證直接存放在
2. Cookie 隨 HTTP 請求夾帶造成的網路頻寬浪費
- 問題原因:每次瀏覽器向同網域發起 HTTP 請求(包含 API 呼叫、HTML 與同網域靜態資源)時,該網域路徑下的所有 Cookie 都會自動夾帶在
CookieRequest Header 中上傳。若開發者將大型 JSON 字串或冗長狀態塞進 Cookie,會直接導致每個請求標頭膨脹(甚至觸發伺服器HTTP 431 Request Header Fields Too Large錯誤),拖慢整體連線速度。 - 解法與檢查步驟:
- 原則:Cookie 僅存放極輕量且伺服器確實需要讀取的識別資訊,單筆與總量應控制在數百 Bytes 至數 KB 內。
- 檢查步驟:開啟瀏覽器開發者工具(F12)切換至 Network 分頁,點選任一 API 請求並查看 Request Headers 中的
Cookie欄位大小;純前端使用的狀態一律改存localStorage或sessionStorage。
3. sessionStorage 在多分頁獨立運作的預期落差
- 問題原因:不少開發者誤以為
sessionStorage在同一個瀏覽器視窗的所有分頁中會即時共用。實際上,sessionStorage是以單一分頁(Tab)為隔離單位;即使透過<a target="_blank">或window.open()開啟同源新分頁時會複製當下那一刻的sessionStorage快照,兩個分頁後續對sessionStorage的修改仍是完全獨立、互不同步的。 - 解法與檢查步驟:
- 原則:若業務邏輯要求「使用者在分頁 A 變更狀態後,分頁 B 也必須同步反映」(例如購物車商品清單、全站登入/登出狀態),切勿使用
sessionStorage,應改用localStorage搭配storage事件或BroadcastChannel API。
- 原則:若業務邏輯要求「使用者在分頁 A 變更狀態後,分頁 B 也必須同步反映」(例如購物車商品清單、全站登入/登出狀態),切勿使用
4. localStorage 超過容量上限(QuotaExceededError)的例外處理
- 問題原因:
localStorage在多數瀏覽器中的同源容量上限約為5MB。當程式無節制寫入大量快取資料(例如塞入 Base64 圖片或龐大 API 回應)超出上限,或在某些舊版瀏覽器無痕/隱私設定下遭到限制時,呼叫setItem()會直接拋出QuotaExceededError(DOMException)。若未以try...catch攔截,將導致前端腳本中斷、畫面卡死。 - 解法與防呆寫法:封裝寫入函式時務必加上
try...catch攔截QuotaExceededError,並在空間不足時主動清理舊快取或降級處理:function safeSetLocalStorage(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); return true; } catch (e) { if (e instanceof DOMException && e.name === "QuotaExceededError") { console.warn("本地儲存空間已達上限,正在清理舊快取..."); localStorage.removeItem("oldCacheKey"); } return false; } }
5. 跨分頁 storage 事件的狀態同步盲點
- 問題原因:當使用者同時開啟同網站的分頁 A 與分頁 B 時,若在分頁 A 修改了
localStorage(例如切換主題或登出),分頁 B 的記憶體狀態不會自動更新,容易造成多視窗操作狀態不一致。 - 解法與防呆寫法:可透過監聽
window.addEventListener('storage', ...)事件實作跨分頁狀態同步。需注意:storage事件只會在「非當前修改分頁」的其他同源分頁觸發,當前執行setItem()的分頁本身不會觸發該事件:window.addEventListener("storage", (event) => { if (event.key === "userPreferences" && event.newValue) { console.log("偵測到其他同源分頁修改了設定:", JSON.parse(event.newValue)); // 同步更新當前分頁的 UI 狀態 } });
總結
總結來說,Cookie、localStorage 與 sessionStorage 各有明確的職責邊界:需要隨 HTTP 請求送往伺服器驗證或搭配 HttpOnly 保護的敏感憑證交給 Cookie;需要跨分頁長期保留的非敏感前端偏好設定交給 localStorage;而僅限單一分頁流程暫存、關閉分頁即應銷毀的表單或檢視狀態,則交給 sessionStorage。依資料機密性、生命週期與作用域精準分工,才能兼顧前端體驗與系統資安。
發表迴響