useMemo快取昂貴的運算
同一份運算(計算範圍內的質數個數),用開關切換走不走 useMemo 快取,再量同一次重新渲染的耗時。
先在 ON 的狀態按幾次,再切到 OFF 按幾次,兩條長條就會拉開。計時用 flushSync 逼 React 同步跑完渲染後在事件處理函式裡量——React 的 Profiler 在正式建置會被移除,量不到東西。
看這篇的來由與原理 →—ms本次重新渲染
- 質數個數
- 4,203
- 渲染次數
- #0

可以自己操作的前端效能實作。數字是當場跑出來的,不是截圖——調參數就會變。之後會陸續增加。
useMemo同一份運算(計算範圍內的質數個數),用開關切換走不走 useMemo 快取,再量同一次重新渲染的耗時。
先在 ON 的狀態按幾次,再切到 OFF 按幾次,兩條長條就會拉開。計時用 flushSync 逼 React 同步跑完渲染後在事件處理函式裡量——React 的 Profiler 在正式建置會被移除,量不到東西。
看這篇的來由與原理 →—ms本次重新渲染
React.lazy列表一次只載一批,捲到底才取下一批。列渲染元件也不在主 bundle 裡,Vite 把它切成獨立 chunk,第一次捲到才透過網路取得。
在框內往下捲,新資料會一批一批逐筆淡入。打開開發者工具的 Network 分頁再按重置重看一次,會看到那個 js 檔在第一次捲到底的瞬間才被請求,首頁載入時完全沒抓它。
看這篇的來由與原理 →Suspense資料未就緒時元件會 throw 一個 promise,React 接住後顯示骨架畫面,等 promise 完成再重試渲染。這是 Suspense 的底層契約。
拉動延遲再重播,骨架畫面停留的時間就會跟著變。這一頁本身也用了預先渲染,首屏 HTML 在建置時就產生好了。
看這篇的來由與原理 →尚未開始請求
虛擬捲動兩側資料完全一樣,都是 10,000 筆在記憶體裡。左邊全部畫進 DOM,右邊只畫可視範圍的約 20 筆,靠位移把它們對到正確位置。
跟 02 是相反的兩件事:02 是少載資料,這裡是少畫 DOM。按下左邊的按鈕會明顯卡一下,那就是瀏覽器在算 10,000 個節點的版面。
看這篇的來由與原理 →節點數差 500 倍。真實專案裡這個差距會直接反映在捲動流暢度與記憶體用量上。
Web Worker同一段質數運算,一邊跑在主執行緒、一邊跑在 Web Worker。運算量與結果完全相同,差別只在「跑在哪條執行緒上」。
盯著上面那個轉圈。按「主執行緒」它會整個凍住,按「Worker」它一路轉不停——那個停頓就是使用者感受到的當機。
看這篇的來由與原理 →Map vs find把每筆訂單對應到使用者名稱。差別在於「怎麼找人」:一種每次都從頭掃陣列,一種先建一張 id 對照表再直接取。兩種做法的結果會互相比對驗證,確保比的是同一件事。
在 map 裡呼叫 find 是列表渲染最常見的效能地雷——它看起來只是一行,實際是 n × m 次比對。資料量翻倍時 find 那側的成本是四倍成長,Map 那側只有兩倍。把資料量拉到 5,000 差距最明顯。
看這篇的來由與原理 →orders.map(o =>
users.find(
u => u.id === o.userId
)
)users 是陣列,只能從頭一個一個比對到找著為止。每筆訂單都重來一次。const index = new Map( users.map(u => [u.id, u]) ) orders.map(o => index.get(o.userId) )先花一次把陣列整理成「id → 使用者」的對照表,之後給 id 就直接取出。像電話簿先按姓氏排好,查的時候直接翻到。
示意:這筆訂單要找 user-9
React.memo一份任務列表,點任一列就重跑該任務、更新它的耗時。右側數字是每一列被渲染過幾次,由 effect 實際計數寫入。
兩個開關都開時點幾列:只有你點的那列會閃、數字會加,其餘全停在 1。把任一個開關關掉再點,整份列表每一列都跟著重畫——明明只有一筆資料變了。useCallback 關掉時尤其容易忽略:函式每次都是新參考,memo 比對後判定 props 變了,就整個失效。
看這篇的來由與原理 →debounce / throttle輸入框每打一個字就算一次「要送出的請求」。三種策略處理同一串輸入,看實際會送出幾次。
快速連打一段字再停手。原始那條會跟你的按鍵數一樣多;防抖只在停手後送一次;節流則在打字期間穩定地送。搜尋建議適合防抖,捲動與拖曳適合節流。
看這篇的來由與原理 →強制同步版面兩種模式做的事完全一樣:把每個方塊的 margin 改掉,也都讀了版面。差別只在讀寫的順序——一種寫完立刻讀,一種全部寫完才讀一次。
寫完就讀(如 offsetHeight、getBoundingClientRect)會逼瀏覽器當場把版面算完才能回答,在迴圈裡做就是每一輪都同步重排一次。一幀的預算約 16ms,超過就撐不住流暢。量測期間畫面會明顯凍住——那正是使用者感受到的當機。這個坑常出現在「遍歷元素、邊量邊改」的程式裡,改法是把讀和寫分開成兩批。
看這篇的來由與原理 →切到「讀寫交錯」後整頁會明顯卡頓——那正是這個 demo 要讓你感受的東西,不是頁面壞了。停止鍵仍然有效,只是回應會慢半拍,多按一下就會停。想少卡一點可以先按停止再切換模式。
AVIF / srcset這是 /surf 實際使用的素材,不是另外做的教學範例。同一張圖輸出成 AVIF 與 WebP、各兩種寬度,位元組是當場抓下來量的。
AVIF 通常比 WebP 再小三到四成;而手機只需要 768 寬那檔,硬塞 1920 給它就是白花好幾倍流量。srcset 的作用就是讓瀏覽器自己挑對的那一檔。
看這篇的來由與原理 →
點左側名稱可切換預覽。實務上不必手動選——sizes 與 srcset 寫對,瀏覽器會依螢幕寬度與像素密度自己決定。