題幹與適用情境
一個多租戶專案管理 Web 應用程式同時支援伺服器端渲染與離線編輯。它需要處理四類狀態:登入工作階段要能被伺服器辨識並跨分頁運作;主題和語言偏好要在下次造訪時保留;多步驟表單草稿只屬於目前分頁,關閉分頁後可以遺失;最多 10000 筆結構化任務記錄和圖片附件要支援離線查詢、修改並在恢復網路後同步。
請在 Cookie、localStorage、sessionStorage 和 IndexedDB 之間分配這些資料,並說明 XSS 與 CSRF、同源隔離、SSR 首屏一致性、多分頁並行、配額與驅逐、資料庫版本升級、登出與本機清理,以及離線同步的處理方式。
10000 筆記錄是用來迫使候選人討論結構化查詢、非同步存取和同步協定的情境限制,不代表瀏覽器保證的容量。題目的核心能力是依據 Web 平台語意做前端狀態持久化決策,因此歸為 frontend。
面試官考察重點
第一,看候選人是否先問「誰要讀取、要活多久、資料是什麼形態、洩漏後果是什麼、誰是事實來源」,再選擇 API。只背容量表無法解釋 SSR、身分驗證和離線一致性。
第二,看能否區分持久化與安全。localStorage 持久不代表適合保存工作階段識別碼;同源腳本可以讀寫它,XSS 既能竊取也能竄改其中的資料。HttpOnly Cookie 可以阻止 JavaScript 讀取工作階段識別碼,但不能阻止已執行的惡意腳本借使用者身分送出請求。
第三,看是否理解 Cookie 的雙向風險。瀏覽器會依匹配規則隨請求傳送 Cookie,伺服器端渲染與身分驗證因此方便;同樣的自動傳送也要求 CSRF 防護。SameSite 是防線之一,不能取代來源驗證、CSRF token 和敏感操作再次驗證等措施。
第四,看能否把 IndexedDB 當作會失敗的本機副本。它適合非同步保存結構化物件、索引和 Blob,但不會自動與伺服器同步;配額、驅逐、清理、升級阻塞和並行衝突都要由應用程式處理。
第五,看是否能給出可執行的驗證矩陣,而不是把「重新整理後還在」當成唯一測試。
回答前需要釐清的問題
- 伺服器首屏需要哪些值? 若 SSR 必須按登入狀態、語言或主題輸出一致的 HTML,這些值需要伺服器可讀來源,或需要接受用戶端掛載後的切換。
- 身分驗證方式是什麼? 本題預設伺服器工作階段。若改成純 API token,還要釐清 token 的簽發、輪替、撤銷和跨站請求模型。
- 草稿真的應按分頁隔離嗎? 若使用者期待關閉後恢復或跨裝置繼續,
sessionStorage就不符合需求,應改用伺服器或 IndexedDB。 - 離線資料是否含敏感內容? 共用裝置、XSS、瀏覽器設定檔和本機備份會改變允許寫入磁碟的資料範圍。
- 10000 筆記錄如何查詢和更新? 需要按專案、更新時間或同步狀態檢索時,索引與交易比簡單鍵值更重要。
- 伺服器是否為最終事實來源? 本題假設伺服器權威,本機副本可以重建;若離線建立的資料不可重建,就要提高持久化、匯出和衝突處理等級。
- 衝突如何定義? 同一筆記錄在多裝置修改,可以採用版本拒絕、欄位合併或業務優先級;儲存 API 本身不會替應用程式決定。
- 登出是否需要清除全部租戶資料? 多帳號共用瀏覽器時,應按使用者和租戶命名空間清理,避免下一位使用者看到上一位使用者的副本。
30 秒回答框架
「我按讀取方、生命週期、資料形態、敏感度和事實來源分配。身分驗證使用伺服器工作階段和帶 Secure、HttpOnly、合適 SameSite 的 __Host- Cookie,並繼續做 CSRF 防護。小型非敏感偏好放 localStorage,SSR 需要時提供伺服器可讀初始值;可捨棄的單一分頁草稿放 sessionStorage;結構化記錄與圖片放 IndexedDB,使用索引、交易、版本化 schema 和 outbox。伺服器保持權威,同步使用版本與冪等鍵並明確解決衝突。所有本機儲存都按可清除、可發生配額失敗處理,並測試 XSS、CSRF、多分頁、驅逐、阻塞升級、重連和登出清理。」
分步深入解答
第一步:用五個問題建立選擇矩陣
先為每類資料回答五個問題:讀取方是伺服器、目前分頁還是同源所有頁面;生命週期是一次渲染、分頁、瀏覽器工作階段還是跨工作階段;資料是小字串、結構化物件還是 Blob;洩漏或竄改後果多大;最終事實在伺服器還是本機。
由此得到本題的初始分配:
登入工作階段 -> 伺服器工作階段 + HttpOnly Cookie 中的隨機工作階段識別碼
主題與語言 -> localStorage;SSR 必需時另有伺服器可讀初始值
單一分頁草稿 -> sessionStorage
離線記錄與圖片 -> IndexedDB + 應用層同步協定這不是按「哪個容量大」排序。Cookie 的關鍵能力是隨請求抵達伺服器;sessionStorage 的關鍵邊界是頂層瀏覽內容;IndexedDB 的關鍵能力是非同步交易、結構化物件和索引。
第二步:讓身分驗證狀態由伺服器權威管理
瀏覽器只保存高熵、短生命週期的工作階段識別碼,真實權限、到期時間和撤銷狀態在伺服器。Cookie 使用 Secure、HttpOnly、適當 SameSite,並盡量採用不帶 Domain、路徑為根的 __Host- 形式。登出、密碼修改和風險事件都在伺服器使工作階段失效,不能只刪除瀏覽器值。
HttpOnly 降低工作階段識別碼被腳本直接讀取並帶走的風險,但同源 XSS 仍可能在目前頁面發起已登入操作。因此仍要做輸出編碼、CSP 等 XSS 防護,並對高風險操作再次驗證。Cookie 會自動隨匹配請求傳送,所以狀態變更請求還應驗證來源並使用 CSRF token;SameSite 不能作為唯一控制。
把工作階段識別碼放進 localStorage 會讓任何成功執行的同源腳本直接讀取它。用戶端加密也不會自動解決問題:若解密金鑰同樣能被頁面 JavaScript 取得,同源 XSS 仍能取得明文或呼叫解密流程。
第三步:用 localStorage 保存小型非敏感偏好
localStorage 按來源隔離,鍵和值是字串,跨瀏覽器工作階段保留;它的讀寫是同步的。主題、語言、表格密度等小型偏好適合放在這裡,但不能假設永久存在,使用者清理網站資料、隱私模式結束或瀏覽器政策都可能移除它。
伺服器不能直接讀取 localStorage。若 SSR 必須在第一幀採用正確語言或主題,可以把經驗證的偏好同步到伺服器可讀 Cookie 或使用者資料,並規定一個權威來源;若只在用戶端讀取,則先輸出穩定預設值,掛載後再切換,避免伺服器 HTML 與首次用戶端渲染不一致。
不要用 localStorage 儲存大量記錄後反覆 JSON.parse 整個陣列。同步序列化和主執行緒 I/O 會隨資料成長影響互動,而且無法取得 IndexedDB 的交易與索引。
第四步:只把可捨棄的分頁狀態放進 sessionStorage
sessionStorage 同時按來源和頂層瀏覽內容分區,在同一分頁重新整理後仍保留,關閉分頁或視窗後清除。因此它適合目前分頁的多步驟表單位置和可捨棄草稿,不適合跨分頁購物車、長期恢復或跨裝置狀態。
一個容易漏掉的邊界是 opener:從現有頁面開啟的新頁面可能先取得 opener 的 sessionStorage 副本,此後兩者分別變化。若草稿絕不能被複製,應移除 opener 關係,或在草稿中加入每個分頁產生的實例 ID,並在恢復時驗證。
sessionStorage 也能被同源腳本讀寫,不能因為「關閉即清除」就保存長期憑證。保存草稿前還應判斷欄位是否包含不應寫入磁碟的密碼、付款或醫療資訊。
第五步:用 IndexedDB 建立可重建的離線副本
IndexedDB 提供非同步請求、交易、物件儲存區、鍵和索引,也能保存 Blob,適合本題的大量結構化記錄和圖片。可以按 tenantId + userId 隔離資料,建立任務、附件和 outbox 三類物件儲存區,並按專案、更新時間、同步狀態建立實際查詢需要的索引。
資料庫升級要使用明確版本和逐版本遷移。開啟新版本時,舊分頁的連線可能阻塞升級;應用程式應監聽 versionchange 並關閉舊連線,向舊頁面提示重新整理,同時處理 blocked,不能讓發布後的頁面永久卡在啟動階段。
IndexedDB 是本機資料庫,不是同步服務。寫入成功只表示本機交易提交。應用程式仍要記錄本機版本、伺服器版本、操作 ID 和同步狀態,並保證重試冪等。
第六步:明確設計離線同步和多分頁衝突
每次離線變更與業務記錄放在同一 IndexedDB 交易中,同時追加 outbox。連線後 worker 按操作 ID 上傳;伺服器以冪等鍵去重,並比較記錄版本。成功確認後再在交易中更新伺服器版本並刪除 outbox 項目,避免「記錄已改但待同步操作遺失」。
衝突策略應來自業務:低風險偏好可採最後寫入者生效,任務狀態可在版本不匹配後提示使用者合併,金流或權限變更則應由伺服器拒絕離線提交。storage 事件或 BroadcastChannel 可以通知其他分頁重新讀取資料,但通知不等於鎖,也不能取代 IndexedDB 交易和伺服器版本檢查。storage 事件不會在執行寫入的同一文件觸發。
第七步:把配額、驅逐和清理當成正常失敗
瀏覽器儲存配額會因瀏覽器、裝置和模式而異。IndexedDB 通常屬於來源的 best-effort 儲存,可能被使用者清除或在儲存壓力下被驅逐;寫入也可能拋出配額錯誤。應用程式可用儲存估算觀察使用量,對不可重建資料謹慎請求持久化,並始終捕捉交易與配額失敗。
策略應包含附件大小上限、最近最少使用清理、伺服器確認後壓縮或刪除舊版本,以及「本機空間不足」的可恢復提示。登出時先在伺服器撤銷工作階段,再關閉資料庫連線並刪除該使用者與租戶的 IndexedDB、偏好和草稿;同時通知其他分頁登出。刪除本機資料本身不能取代伺服器撤銷。
第八步:用失敗矩陣驗證邊界
驗證至少涵蓋:SSR 首屏與水合一致;重新整理、複製分頁、新開分頁和關閉分頁;另一個分頁登出;XSS 情境下腳本可讀範圍;跨站狀態變更請求;隱私模式;網站資料被清理;配額超限;舊分頁阻塞資料庫升級;兩個分頁同時編輯;離線重試、重複回應、亂序回應和衝突;切換租戶後舊資料不可見。
通過標準不只是「資料還在」。工作階段必須可撤銷,敏感值不能暴露給 JavaScript,草稿遵守分頁邊界,離線寫入不會遺失 outbox,重複同步不產生重複業務效果,升級可恢復,空間不足有降級路徑,伺服器可以重建本機副本。
高品質示範回答
「我不會先按容量選,而會先看讀取方、生命週期、資料模型、信任邊界和事實來源。登入工作階段需要伺服器和所有分頁辨識,所以真實工作階段保存在伺服器,瀏覽器只持有 Secure、HttpOnly、合適 SameSite 的 __Host- Cookie。它降低 token 被 JavaScript 讀取的機會,但 XSS 仍能代使用者操作,Cookie 自動傳送也需要 CSRF token、來源驗證和敏感操作再次驗證。
主題與語言是小型非敏感偏好,放 localStorage;如果 SSR 第一幀必須使用它們,我會同步一份經過驗證的伺服器可讀偏好並規定權威來源。目前分頁的可捨棄表單草稿放 sessionStorage,重新整理可恢復,關閉即清除;同時處理新頁面從 opener 取得初始副本的邊界。
10000 筆任務和圖片放 IndexedDB。資料庫按使用者與租戶隔離,使用版本化 schema、索引和交易。每次業務修改與 outbox 在同一交易提交,恢復網路後用操作 ID 冪等上傳,伺服器比較記錄版本並回傳確認或衝突。跨分頁通知只觸發重新讀取,最終一致性由本機交易與伺服器版本控制保證。
我會把所有本機儲存視為可清除、可發生配額失敗且可被同源腳本存取。實作配額捕捉、附件清理、升級時關閉舊連線,以及先撤銷伺服器工作階段再清理使用者命名空間的登出流程。最後測試 SSR、水合、分頁複製、XSS、CSRF、配額、驅逐、阻塞升級、並行編輯、離線重試和租戶切換。」
常見錯誤
- 按容量表機械選擇 → 忽略伺服器讀取、分頁邊界和資料權威 → 先使用五維選擇矩陣。
- 把工作階段識別碼放進
localStorage→ 同源 XSS 可直接讀取並帶走 → 使用伺服器工作階段和受保護 Cookie,並繼續防 XSS。 - 認為
HttpOnly消滅 XSS → 惡意腳本仍可發起已登入操作 → 把防竊取與防代操作分開。 - 只設定
SameSite就不做 CSRF → 瀏覽器政策、請求類型和業務流程仍有邊界 → 組合 token、來源驗證和再次驗證。 - 伺服器直接依賴
localStorage→ SSR 無法讀取瀏覽器儲存 → 提供伺服器可讀初始值或接受掛載後切換。 - 認為
sessionStorage新分頁必定為空 → opener 可能帶來初始副本 → 移除 opener 或加入分頁實例識別碼。 - 把大陣列塞進
localStorage→ 同步解析、整包重寫且沒有索引交易 → 改用 IndexedDB。 - 認為 IndexedDB 會自動同步 → 本機提交與伺服器確認是兩件事 → 實作 outbox、冪等、版本和衝突策略。
- 用跨分頁通知當分散式鎖 → 訊息可能延遲,且不能決定伺服器衝突 → 通知後重讀,用交易和版本做正確性控制。
- 假設本機資料永久存在 → 清理、驅逐、隱私模式和配額都可能讓它消失 → 允許重建並處理寫入失敗。
- 登出時只清 Cookie → IndexedDB 和偏好仍可能洩漏給下一個帳號 → 伺服器撤銷後按使用者與租戶清理全部本機副本。
追問及應對
追問一:把 token 加密後放 localStorage 可以嗎?
如果頁面 JavaScript 能取得解密金鑰或呼叫解密流程,成功執行的同源惡意腳本通常也能做到,所以這不能解決本題的 XSS 工作階段竊取問題。更穩妥的邊界是伺服器工作階段加 HttpOnly Cookie,並結合 XSS、CSRF、輪替與撤銷控制。用戶端加密只有在金鑰不暴露給同一攻擊面時才解決對應威脅。
追問二:使用者要求草稿關閉分頁後還能恢復怎麼辦?
生命週期限制已經改變,sessionStorage 不再適用。非敏感且只需本機恢復的草稿可放 IndexedDB;需要跨裝置或不可遺失時應同步到伺服器。無論選哪一種,都要定義保留期、帳號與租戶隔離,以及敏感欄位不寫入磁碟的規則。
追問三:兩個分頁同時修改同一筆離線任務怎麼辦?
每筆記錄保存伺服器版本和本機修訂號,寫入 IndexedDB 時使用交易。分頁透過 BroadcastChannel 或資料變更通知其他頁面重新讀取,但伺服器在同步時仍以版本做條件更新。版本衝突後依業務選擇欄位合併、提示使用者或拒絕;不能把「最後收到通知的分頁」當作事實來源。
追問四:IndexedDB 升級被舊分頁阻塞怎麼辦?
舊連線監聽 versionchange 後主動關閉並提示重新整理;新頁面監聽 blocked,顯示可恢復狀態而不是無限等待。遷移保持逐版本、冪等且能處理部分歷史資料。發布前用一個舊版本分頁保持連線,再開啟新版本驗證關閉、提示和遷移路徑。
追問五:瀏覽器空間不足時如何降級?
捕捉配額與交易錯誤,先停止快取新附件,再清理已由伺服器確認且可重建的舊附件和歷史版本。保留未同步 outbox 的優先級高於可下載快取。仍不足時明確提示使用者連線同步或釋放空間,不能靜默宣稱保存成功。
追問六:登出的正確順序是什麼?
先請求伺服器撤銷目前工作階段,使已複製的 Cookie 或仍開啟的分頁也失效;接著通知其他分頁,停止同步任務,關閉 IndexedDB 連線,按使用者和租戶刪除資料庫、草稿與偏好,最後跳轉到未登入狀態。網路失敗時不能只做本機「假登出」,應標明工作階段撤銷尚未確認並限制繼續操作。