先產品仍需要備份出口)
把筆記放在瀏覽器里很適合一個不想強迫用戶注冊的小產品打開即寫離線可讀也少了一套賬號服務。可是“本地優(yōu)先”容易被誤說成“永不丟失”。瀏覽器緩存、用戶清理、隱私窗口、設備損壞都不會因為產品小而繞開它。保存狀態(tài)與備份狀態(tài)分開顯示MDN 的存儲與清理說明區(qū)分默認的 best-effort 存儲與持久化申請。申請成功也不阻止用戶主動刪除數據。界面上可以顯示“已保存到此瀏覽器”不該直接寫“永久保存”。另一個明確動作應該是“導出備份”。我會讓第一版產品保存一份帶版本號的 JSON并提供導入。版本號讓未來遷移有落腳點字段檢查則避免把損壞文件或其他產品的數據當成自己的筆記。導入前先預覽數量不悄悄覆蓋現有記錄。同步、加密和備份是三件事不能靠一個云朵圖標混在一起。賬號并不是默認答案Chrome 在 10 月 5 日介紹的 Email Verification API 變更仍涉及 origin trial 和版本差異。對于一個小型離線工具這類登錄能力可以關注卻不適合當作所有瀏覽器已經具備的基礎設施。先把數據帶得走往往比先加登錄更能降低用戶的遷移成本。下面的代碼通過注入 storage 接口演示保存與載入。Node 中用內存對象做替身瀏覽器使用時才替換為localStorage并在調用處捕獲配額、權限等異常。測試替身能驗證序列化與校驗流程不能證明瀏覽器不會清理數據。執(zhí)行node notes.js驗證保存后重載、壞版本拒絕和重復 ID 拒絕。本例沒有跨設備同步沒有測 IndexedDB也沒有將備份文件寫到云端。小產品不必一開始就解決所有災難但必須給用戶一個清楚、可以親手拿走的出口??瑟毩⑦\行的核心例子function decode(raw) { const xJSON.parse(raw); if(x.version!1||!Array.isArray(x.notes)||x.notes.length1000) throw Error(不支持的備份格式); const idsnew Set(); for(const n of x.notes) { if(typeof n.id!string||!n.id||ids.has(n.id)|| typeof n.text!string||n.text.length20000) throw Error(筆記字段無效); ids.add(n.id); } return x; } function repository(storage) { return { save(notes) {const rawJSON.stringify({version:1,notes});decode(raw);storage.setItem(notes-v1,raw);return raw;}, load() {const rawstorage.getItem(notes-v1);return raw?decode(raw).notes:[];} }; } const memorynew Map(); const storage{setItem:(k,v)memory.set(k,v),getItem:kmemory.get(k)??null}; const reporepository(storage); const backuprepo.save([{id:a,text:給下一次創(chuàng)作留一頁白紙}]); if(repository(storage).load()[0].id!a)throw Error(重載失敗); for(const bad of [{version:2,notes:[]}, {version:1,notes:[{id:a,text:1},{id:a,text:2}]}]) { let rejectedfalse;try{decode(bad);}catch{rejectedtrue;} if(!rejected)throw Error(壞備份應被拒絕); } console.log(backup);