iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Software Development

從零打造網頁簡報編輯器系列 第 29 篇

Day 29|量測大型簡報的操作歷史成本

  • 分享至 

  • xImage
  •  

系列:一張投影片背後的工程:從零打造網頁簡報編輯器

對應程式:2026-10-04 工作目錄,package.json 版本 0.20.0。本文數據來自當日 Edge 實測,原始資料另存 JSON;不以測試執行時間推算操作延遲。

前幾篇讓編排功能變得更完整,今天換一個問題:文件變大以後,一段文字修改會留下多少歷史?Day 21 曾補充連續輸入合併,這次我用固定資料比較它實際減少了什麼。

結果先說:在這次條件下,合併明確減少了保留的歷史與物件版本;純 reducer 的耗時差距很小,還不能宣稱使用者輸入因此變快。

把資料量和測量範圍固定下來

我用取證腳本在獨立 Edge 設定檔載入目前專案的實際 reducer,建立三組資料:

頁數 每頁內容 總物件數
10 15 個文字、4 個矩形、1 個圖片引用 200
50 相同組成 1,000
100 相同組成 2,000

每組都對第一頁的同一個文字框提交 500 次不同內容,文字長度保持一致。這是固定負載,沒有模擬真實逐字增長,也沒有產生 500 次鍵盤事件。

對照組不傳 groupKey,每次更新各留一步,最後受 50 筆歷史上限限制;合併組全部傳相同的 groupKey。兩組都使用目前版本的同一個 reducer,不是切換歷史版本比較。

測量環境為 Windows、AMD Ryzen 7 9800X3D、Edge 154.0.4258.53,使用 Vite 開發伺服器、無介面 Edge 並停用 GPU。每種模式先暖機三次,再交錯順序取九次計時樣本;表中是中位數。

先看實際保留了哪些物件

500 次更新後,對照組保留 50 筆 past,合併組保留 1 筆。兩組最後的文字都必須是「測量文字 0499」,量測時也檢查這個結果,避免把漏做更新當成變快。

以十頁為例,從 past 加上 present 出發,用 Set 依物件身分計算:

項目 每次獨立記錄 同一欄位合併
過去文件版本 50 1
不同投影片物件 60 11
不同文字/圖片/圖形物件 250 201

十頁原本共有 200 個物件,最後沒有變成 51 × 200 個副本。沒有修改的投影片與物件仍共用參照;更新集中在第一頁的那個文字框。

這比把每份歷史 JSON 的長度全部相加更接近實際保留關係。JSON 序列化會展開重複內容,不能直接把它的總長度當作 JavaScript heap 使用量。

耗時與記憶體要分開讀

頁數 獨立記錄:500 次更新 合併:500 次更新 獨立記錄:保留 heap 增量 合併:保留 heap 增量
10 0.3 ms 0.2 ms 13,732 bytes 336 bytes
50 0.8 ms 0.7 ms 32,280 bytes 10,484 bytes
100 1.4 ms 1.4 ms 37,492 bytes 6,096 bytes

時間是整批 500 次同步 reducer 呼叫,沒有包含 React 更新畫面、IndexedDB、自動儲存或圖片解碼。數值接近計時解析度,不能把相差 0.1 毫秒解讀成穩定的使用者體感改善。

記憶體則在強制垃圾回收後,讀取 Runtime.getHeapUsage 的 usedSize,比較保留最後一份 history 前後的差值,重做五次取中位數。基礎文件與預先建立的 actions 已存在於兩邊的基準中。這個 API 回報 JavaScript heap 資訊,不是整個瀏覽器行程的記憶體。Chrome DevTools Protocol:Runtime.getHeapUsage

像五十頁合併組的增量高於一百頁,就提醒我:這裡還包含 V8 配置與回收的測量雜訊,不能拿幾個數字推導線性公式。比較可靠的結論,是本次各組都減少了保留量,而 50 步降為 1 步的歷史行為是可直接驗證的。

合併省下保留量,仍然要計算每次更新

實作中決定是否延續歷史的部分如下:

const continuesGroup = action.groupKey !== undefined &&
  action.groupKey === history.groupKey

past: continuesGroup
  ? history.past
  : [...history.past, history.present].slice(-HISTORY_LIMIT)

即使沿用 past,每次仍會呼叫 presentationReducer() 建立新的 present,並保留不可變更新的行為。因此「只留一步」不等於「只執行一次更新」。

原本連續輸入容易快速耗盡 50 步,讓先前其他操作擠出歷史;合併既降低保留成本,也讓一次復原能退回整段編輯。這次驗證的是已完成的改進效果,沒有為了讓數字好看而把必要更新跳過。

額外確認十頁確實能顯示

除了純函式量測,腳本也把十頁、每頁二十個物件存進 IndexedDB,再重新載入編輯器。十張縮圖、十張列印頁,以及十個圖片引用都存在,PNG 成功解碼。
https://ithelp.ithome.com.tw/upload/images/20261009/20182759RF4c04hoyk.png

這十頁共用一筆合成 PNG 資產。它驗證此資料集的載入與呈現,不代表完成 20 MB 圖片、多張不同大圖或一百頁畫面流暢度驗收。

本次沒有量測拖曳幀率、輸入到畫面更新的延遲、儲存等待或整個瀏覽器的峰值記憶體。頁數增加時 reducer 批次耗時上升,值得後續追查;目前證據還不足以認定它就是整個編輯器的主要瓶頸。

讀這些數據時可以核對什麼

上面的資料組成、更新次數、兩組唯一的操作差異、歷史筆數、物件身分數與中位數,都寫在本文中。讀者可以據此判斷結論是否超過測量範圍:這次支持「歷史保留量減少」,沒有證明整個編輯器的畫面更新變快。

完整九次計時與五次記憶體樣本目前只保存在我的本機驗證資料中。文章尚未提供公開的程式或原始數據連結,所以這些數字現階段不能由讀者下載腳本逐項重跑。下一篇會用可見的 Demo 畫面整理作品成果,並清楚區分文章能展示的內容與尚未公開的檔案。


上一篇
Day 28|用圖層面板找回被遮住的物件
系列文
從零打造網頁簡報編輯器 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言