系列:一張投影片背後的工程:從零打造網頁簡報編輯器
對應程式版本:Git tag
v0.4.0版本說明:本文記錄第一版的本機資料庫、啟動還原與排隊自動儲存流程,並補充這一版讀取失敗時會覆蓋資料的問題與後續修正。
簡報文件是結構化資料,圖片卻是 Blob。全部塞進一段 JSON 會讓每次改一個字都重寫大型圖片;只保存圖片網址,重新整理後又可能找不到檔案。因此這一版用 IndexedDB 分開保存文件與圖片資產。
資料庫名稱是 web-slide-editor,版本為 1,第一次開啟時建立兩個 object store:
const PRESENTATIONS_STORE = 'presentations'
const ASSETS_STORE = 'assets'
const ACTIVE_PRESENTATION_ID = 'active'
presentations 保存目前文件與更新時間;assets 以資產 ID 保存 Blob、MIME、檔名、原始寬高與建立時間。文件中的圖片物件只保留 assetId,因此文字修改不必複製圖片內容。

需要一起匯入文件與圖片時,saveProject 會開啟涵蓋兩個 object store 的同一筆 readwrite transaction。任一寫入失敗,整筆交易便不會留下半套專案。
程式啟動後先呼叫 loadPresentation(),等讀取結束才把 hydrated 設為 true;自動儲存要等 hydrated 為 true 才會開始計時。這個順序很重要,否則初始空白文件可能在舊文件讀完前先被自動儲存覆蓋。
文件變更後等待 400 毫秒再寫入,連續操作會取消前一個計時器。真正的寫入還會排進同一條 Promise queue,避免較慢的舊存檔在較新的版本之後才完成。revision 編號只負責讓畫面上的「已儲存」狀態對應最新一次請求。
不過這一版只擋住讀取成功與沒有舊資料兩種情況:讀取失敗的 catch 同樣會把 hydrated 設為 true。loadPresentation() 會用 isPresentationDocument 驗證讀到的文件,一旦驗證失敗,例如平均分布把較寬的物件推到畫布外、x 變成負數,畫面就停在初始空白文件;400 毫秒後自動儲存用它蓋掉原本的資料,畫面上的儲存狀態最後還會顯示「已儲存在這台裝置」。後來的修正(commit 4bd1dbd)從三處補上:平均分布不再把物件推出選取範圍;寫入前先驗證文件,讀不回來的內容不會存進去;讀取失敗時暫停自動儲存並保留原資料,直到使用者匯入備份。
儲存測試建立一個含圖片的文件,分別寫入文件與 PNG Blob,再重新讀取兩者。測試除了比較文件,也確認資產檔名、尺寸與 Blob 大小;另一個案例則驗證 saveProject 能在一次操作中保存匯入文件與資產。
這些資料保存在目前瀏覽器設定檔,不是雲端同步。重新整理或重新開機後仍可讀取,但清除網站資料、更換瀏覽器或裝置時不會自動帶走,因此下一步仍需要可下載的專案備份。