Onigiri the cat
ONIGIRICode Supervisor
original post2026-08-15
Onigiri the cat

Haru Li

Frontend Developer

14 posts

·2 min read效能瀏覽器

那個轉圈停住的瞬間,就是使用者眼中的當機

效能問題裡最難用數字說明的一種是「頁面沒有壞,只是不動了」。耗時 800 ms 聽起來還好,但那 800 ms 裡點什麼都沒反應,感覺就是當機。

所以這個 demo 我沒有從數字下手,從一個轉圈下手。

發想:讓「卡住」看得見

一樣的運算,一邊跑在主執行緒、一邊跑在 Web Worker。運算量、結果完全相同,唯一的差別是跑在哪條執行緒上

畫面上方放一個一直在轉的圈。它是純 CSS 動畫,理論上跟你的按鈕無關。

測試:盯著那個圈

按「在主執行緒算」——轉圈整個凍住,直到運算結束才恢復。

按「在 Worker 算」——轉圈一路轉不停,運算照樣完成,耗時跟上面差不多。

兩邊都會顯示實際耗時與質數個數,證明做的是同一件事。

理解:CSS 動畫為什麼也會停

「CSS 動畫跑在合成執行緒,不會被 JS 影響」——這句話只對了一半。

transformopacity 的動畫確實可以交給合成執行緒。但動畫的啟動、狀態更新、以及任何需要主執行緒參與的環節仍然會被卡住。更關鍵的是:主執行緒被佔住時,瀏覽器沒辦法處理任何事件,你的點擊、捲動、輸入全都排在後面等。

JavaScript 是單執行緒的。那段質數運算跑起來的時候,主執行緒沒有任何餘裕去做別的事——它不是「比較慢」,是完全沒空

Web Worker 是另一條真正獨立的執行緒。主執行緒把工作丟過去,自己繼續處理事件與畫面,等 Worker 算完再用訊息把結果送回來。

const worker = new Worker(new URL("./primes.worker.ts", import.meta.url), {
  type: "module",
});
worker.postMessage({ limit });
worker.onmessage = (e) => setResult(e.data);

講解:代價與界線

Worker 不是免費的:

  • 沒有 DOM。 Worker 裡碰不到 document,只能算完把結果傳回來。
  • 資料要複製。 傳過去的東西會被結構化複製,傳大量資料本身就有成本(可以用 Transferable 物件避開,但更麻煩)。
  • 啟動有開銷。 為了一個 5 ms 的運算開 Worker,得不償失。

判準是:這段運算會不會讓主執行緒停超過一幀(約 16 ms)? 會的話才值得考慮搬走。

典型適合的場景:大量資料的解析與轉換、影像處理、加解密、複雜的搜尋或排序。


這個 demo 可以在 效能實驗室 自己操作。

Every post here lives as a markdown file in this repo — publishing is adding a file.

© 2026 HARU LI