題目與情境
你負責多步驟申請表。使用者會切換分頁、鎖定手機、返回上一頁,或離開頁面數小時。瀏覽器可能凍結頁面、從 bfcache 恢復,或因記憶體壓力捨棄頁面。表單需要恢復草稿,且不能覆蓋較新的伺服器資料。
假設表單包含敏感但非受監管資料,使用者可能在多台裝置登入,網路也可能間歇中斷。回答必須說明哪些是盡力而為,哪些才是權威狀態。
面試官在考察什麼
- 能否區分可見性、凍結、頁面捨棄與 bfcache 恢復。
- 是否避免把
unload或beforeunload當作可靠持久化鉤子。 - 本地草稿是否有歸屬、版本、過期與衝突規則。
- 恢復時能否保留使用者意圖,不靜默替換較新的伺服器資料。
作答前的釐清問題
- 遺失本地草稿是否可以接受,還是必須提供持久恢復保證?這決定本地儲存是便利功能還是快取。
- 同一份草稿能否在多台裝置編輯?若可以,伺服器需要版本或衝突策略。
- 資料是否適合本地保存?敏感欄位可能需要加密、選擇性省略或完全不持久化。
- 提交合約是什麼?草稿保存與最終提交需要不同的冪等與驗證規則。
30 秒回答框架
「我把伺服器作為權威狀態,把本地草稿作為有界恢復快取。輸入變化經過防抖後保存,文件變為 hidden 時再盡力刷新伺服器。用 pageshow 在 bfcache 恢復後重新驗證,用 resume 在凍結後刷新過期資料。我不依賴 unload。草稿記錄過期時間、Schema 版本、伺服器基線版本與髒欄位;發生衝突時明確展示或合併,不靜默覆蓋。」
分步深入解答
1. 建模生命週期狀態
visibilitychange 表示頁面變為隱藏或可見,但不保證 JavaScript 會繼續執行。瀏覽器可能凍結隱藏頁面,被捨棄的頁面甚至不會執行清理。pagehide 和 pageshow 描述導覽與 bfcache 恢復,freeze 和 resume 暴露 Chromium 的生命週期轉換。
因此應在風險到來前保存,而不是等待最後一刻的 unload。文件變為 hidden 時安排小型本地寫入和盡力網路刷新;pagehide 時停止非必要工作並記錄檢查點;pageshow 時檢查 event.persisted,在展示恢復表單前重新驗證伺服器版本。
2. 讓本地草稿有界且帶版本
只保存適合恢復的欄位。草稿記錄可以包含:
draft_id, user_id, form_schema, base_server_version,
changed_at, expires_at, dirty_fields, values結構化資料使用 IndexedDB,並用小型本地索引查找。寫入要防抖,避免每次輸入都寫一次;限制載荷大小並刪除過期草稿。Schema 版本讓應用程式可以遷移或捨棄不相容記錄,而不是把舊記錄當成目前格式解析。
本地記錄不是權威狀態。它是裝置上的快取,可能缺失、過期、重複,或被瀏覽器刪除。
3. 頁面隱藏時安全刷新
文件隱藏時先提交本地檢查點,再嘗試小型的驗證網路請求。請求攜帶草稿 ID 和基於哪個伺服器版本產生。只有版本符合時伺服器才接受,並回傳新版本。
不要讓頁面等待長請求後才能導覽。請求無法完成時,本地檢查點仍能保護裝置上的內容。避免同步 unload 請求,它會損害導覽體驗,也不能覆蓋行動生命週期路徑。
4. 處理 bfcache 與凍結後的恢復
當 pageshow 帶有 persisted 時,頁面可能在視覺上完整、邏輯上卻已過期。重新取得伺服器版本,與表單基線比較;若另一台裝置改過草稿,就顯示衝突選擇。resume 觸發時刷新頁面凍結期間可能過期的工作階段和資料。
被捨棄的頁面沒有可恢復的記憶體狀態。下次載入時查找未過期本地草稿,把它的基線版本與伺服器比較,並提供恢復、捨棄或合併選項。合併前要讓使用者看到哪些值更新了。
5. 測試失敗路徑和隱私
測試切換分頁、行動應用程式切換、前進後退、bfcache 恢復、瀏覽器捨棄、離線編輯、配額耗盡、Schema 遷移、草稿過期和雙裝置衝突。必須斷言較新的伺服器版本不會被靜默覆蓋。
記錄中要脫敏草稿欄位。刪除帳戶時清理草稿,設定短保留期,並在隱私說明中解釋本地恢復。「已保存」提示只能代表已確認的檢查點,不能代表 unload 處理器可能執行過。監控恢復成功率、衝突率、本地寫入失敗和伺服器保存延遲。
高品質示範回答
我會讓伺服器版本化並作為權威來源,本地只保存有界恢復草稿。輸入變化經過防抖後寫入 IndexedDB,記錄 Schema 版本、過期時間、髒欄位集合和伺服器基線。visibilitychange 變為 hidden 時寫檢查點並嘗試小型驗證保存,但不阻塞導覽,也不依賴 unload。
pageshow 觸發時,尤其是從 bfcache 恢復時,先重新取得伺服器版本再信任恢復表單。resume 時刷新凍結期間可能過期的資料與工作階段。頁面被捨棄後從新載入開始,提供未過期本地草稿。版本不一致時顯示衝突介面或逐欄位合併,絕不靜默覆蓋較新的伺服器副本。測試涵蓋行動端捨棄、離線、配額限制和雙裝置衝突。
常見失誤
- 錯誤表現:只在
beforeunload保存 → 失敗原因:行動瀏覽器可能不觸發,而且會阻礙 bfcache → 修正方法:在輸入變化和 hidden 轉換時建立檢查點。 - 錯誤表現:把可見的 bfcache 頁面當成最新 → 失敗原因:快照可能含有過期伺服器狀態 → 修正方法:在
pageshow重新驗證並比較版本。 - 錯誤表現:把本地儲存當成權威狀態 → 失敗原因:它可能被清理、過期或不可用 → 修正方法:以伺服器版本為準,把本地資料呈現為恢復候選。
- 錯誤表現:在更新版本上重試整個表單 → 失敗原因:會覆蓋無關編輯 → 修正方法:攜帶樂觀版本只傳送髒欄位並解決衝突。
- 錯誤表現:為了除錯記錄草稿值 → 失敗原因:敏感內容會洩露到遙測 → 修正方法:只記錄 ID、版本、大小和結果。
追問與回答
應該用 localStorage 還是 IndexedDB?
localStorage 只適合很小的同步中繼資料。結構化草稿、較大載荷和非同步寫入使用 IndexedDB。兩者都不提供持久保證或跨裝置權威性,都需要過期、配額、Schema 版本和隱私規則。
如果使用者在兩台裝置離線編輯怎麼辦?
每次保存攜帶伺服器基線版本與裝置或草稿 ID。恢復連線時只接受版本符合的寫入,然後顯示逐欄位合併或讓使用者選擇。只有產品明確接受靜默遺失且欄位相互獨立時,才可使用最後寫入勝出。
Service Worker 能保證最後一次保存嗎?
不能。Service Worker 可以提高重試投遞率,但可能被停止、沒有網路,或在某條流程中不可用。介面必須顯示已確認的檢查點,伺服器必須讓重試冪等。本地草稿仍是請求無法完成時的裝置兜底。