上集認完了瀏覽器這支五人隊——隊長、打野、守線英雄、輸出核心、工具人。每個分頁派一個守線英雄,被網站隔離跟沙箱關得死死的,所以一個分頁崩了不會帶走全家。

這集講他們怎麼打一場團戰

因為「把畫面顯示出來」這件事,不是一次就結束——只要滑頁面、有動畫、有東西在動,這支隊伍就得一秒鐘重打 60 次團戰。而且每一波,他們只有大約 16 毫秒可以打完整套連招。打完了,畫面順順的;打不完,就是那個卡卡的、一格一格的「掉幀」。

這 16 毫秒怎麼來的?很簡單的算術:螢幕一秒要更新 60 次(60 幀),1000 毫秒 ÷ 60 ≈ 16.7 毫秒一幀。在這個時間視窗內,這支隊伍要從一坨 HTML 字串,一路打到螢幕上的像素。

(還沒讀上集的話,先去認人:瀏覽器其實是一支五人隊(上)。這篇預設讀者已經知道守線英雄是誰。)


🏰 第 0 層:全景圖 — 一套連招,每秒打 60 次

⚔️ Level 0 / 5 瀏覽器這支五人隊(下)
0% 完成

守線英雄要把畫面打出來,這套連招的順序是固定的,一步接一步:

瀏覽器的繪製流程:DOM → 樣式 → 排版 → 上色 → 合成

關鍵在於:這套連招的前半段(解析、樣式、排版、上色)是守線英雄自己一個人(主執行緒)扛的,而後半段(合成)有副手(合成執行緒)跟輸出核心幫忙。這個分工是整篇的重點——它決定了為什麼有些畫面更新很順、有些卻會卡。

Mogu 偷偷說:

很多人以為「瀏覽器把網頁畫出來」是一個動作,按一下就好。其實它是一套每秒重複 60 次的連招。

你現在滑這篇文章,每往下滑一點,這套連招就在背景跑一遍。你覺得很順,是因為這支隊伍每次都在 16 毫秒內打完。哪天你滑某個網頁覺得「怎麼卡卡的」,不是你手機爛,是那支隊伍有人拖到後腿、連招接不完——下面會抓出兇手是誰 (⁠¬⁠‿⁠¬⁠)


🏰 第 1 層:連招前搖 — 守線英雄一個人扛的活

⚔️ Level 1 / 5 瀏覽器這支五人隊(下)
20% 完成

連招的前半段,全部壓在守線英雄的主執行緒main thread)這一個人身上。它要做三件事:

  1. 解析 HTML → DOM:把一串文字標籤變成一棵樹狀結構(DOM),代表頁面的骨架。這個解析是邊下載邊做的,HTML 還沒下載完就能先開始建。
  2. 解析 CSS → CSSOM:把樣式表變成另一棵樹,代表所有樣式規則。
  3. 算最終樣式:把 DOM 跟 CSSOM 合起來,算出「每個元素最後到底長什麼樣」(什麼顏色、多大、什麼字體)。

但這裡有個會打斷前搖的陷阱:<script> 會卡住解析。瀏覽器解析 HTML 解到一半,撞到一個沒加 defer/async<script>,會停下來先把這段 JS 跑完,才繼續解析。因為這段 JS 可能會改 DOM,瀏覽器不敢往下走。

Mogu 畫重點:

「為什麼我網頁第一秒是白的?」十之八九就是這個——某個會擋住畫面的 <script> 卡在前面,守線英雄被迫先去跑 JS,畫面只好乾等。

加一個 defer 屬性,常常就是「白畫面三秒」變「白畫面零秒」的差別。一個字的修改,效能差一個數量級。前端的世界就是這麼樸實無華又樸實無華 (⁠ ̄⁠▽⁠ ̄⁠)


🏰 第 2 層:排兵布陣 + 上色 — 還是主執行緒的活

⚔️ Level 2 / 5 瀏覽器這支五人隊(下)
40% 完成

前搖比完,守線英雄知道「場上有什麼、每個東西長怎樣」了。接下來還是它一個人的活:

排版(layout,又叫 reflow:算出每一個元素的方框確切站在哪(x, y 座標)、多大(寬、高)。聽起來簡單,其實超複雜——要處理文繞圖、彈性盒、格線、行高、邊界重疊一堆規則。瀏覽器引擎有整個團隊花好幾年就在搞這一步。

這一步很貴。而且關鍵是:它會被牽連重算。如果等下有 JS 改了某個元素的大小、或塞了新內容進去,瀏覽器可能得把一部分(最慘是整頁)的排版重算一遍。這就是「排版很貴,少碰為妙」這句效能金句的由來。

上色(paint:排版算完位置,接著守線英雄走一遍版面樹,產生一份「畫圖指令清單」(顯示列表,display list)——「在這個座標畫一個藍色方塊、然後在那裡畫一行字、再疊一張圖」,照 z-index 順序排好,疊放關係才不會錯。

注意:到這裡還沒有任何一個像素被畫到螢幕上。守線英雄只是把「要畫什麼」這份清單列好了。真正的下筆,是下一層的事。

Mogu 真心話:

有沒有發現,到目前為止累死累活的都是守線英雄一個人(主執行緒):解析、算樣式、排版、列上色清單,全是它扛。

這就是為什麼前端優化老是在講「別讓主執行緒太忙」。它是隊上最忙的人,你又丟一堆肥 JS 給它跑,它就排不出時間比連招、畫面就卡。把工作從主執行緒搬走(搬給副手或 GPU),是效能優化的核心思路。記住這個人,下一層就要看誰能幫它分擔 ʕ⁠•⁠ᴥ⁠•⁠ʔ

小測驗

到「上色」這一步結束,螢幕上畫好像素了嗎?


🏰 第 3 層:收頭 — 副手登場,為什麼滑動可以很順

⚔️ Level 3 / 5 瀏覽器這支五人隊(下)
60% 完成

清單列好,前半段結束。連招的收頭,守線英雄不再一個人扛了——它把活交給同一個進程裡的另一雙手:合成執行緒compositor thread)。

這是整篇最重要的一個設計:合成執行緒跟主執行緒是分開的兩條線。 也就是說,就算主執行緒正忙著跑一坨肥 JS、卡得要死,合成執行緒還是能繼續動。

它做這幾件事:

  1. 分圖層:把頁面拆成幾個可以獨立處理的圖層(layer)。會動的、有 transform 的、影片、canvas 通常各自一層。
  2. 點陣化:把每個圖層切成一塊塊小圖磚(tile,常見 256×256 或 512×512),丟給一票點陣化小工(raster worker)平行處理,用繪圖函式庫 Skia 把「畫圖指令」真的變成像素點,存進 GPU 記憶體。
  3. 合成出一幀:把所有圖磚組成一個「合成幀」,透過跨進程通訊送給隊長,最後由輸出核心(GPU 進程)用顯卡把這些圖層疊好、貼到螢幕上。
Mogu 內心戲:

這個「主執行緒卡死、滑動還是很順」的現象,第一次知道的人都會愣一下:蛤,畫面在動不就代表瀏覽器沒當嗎?

不一定。滑動順是合成執行緒的功勞,它跟跑你 JS 的主執行緒是兩個人。所以你可能滑得很爽,但一點按鈕發現「咦怎麼沒反應」——因為按鈕的事件要主執行緒處理,而它正埋在你那段 for 迴圈裡出不來。畫面順 ≠ 網頁活著,這是兩條命 (⁠⌐⁠■⁠_⁠■⁠)


🏰 第 4 層:16 毫秒的生死 — 為什麼有的動畫順、有的卡

⚔️ Level 4 / 5 瀏覽器這支五人隊(下)
80% 完成

現在懂了「主執行緒 vs 合成執行緒」這條分界線,就能解開前端最常見的效能謎題:為什麼同樣是做動畫,有的滑順如絲、有的卡成幻燈片?

答案就看:這個動畫需不需要驚動忙碌的主執行緒。

順的動畫(只走合成執行緒):只改 transform(位移、縮放、旋轉)或 opacity(透明度)。這兩個性質不影響排版、不影響上色——合成執行緒手上已經有那個圖層的像素了,它只要把圖層挪個位置、改個透明度,就能生出新的一幀。主執行緒就算在忙,這種動畫照樣 60 幀順順跑。

卡的動畫(綁死主執行緒):去動 heighttopwidthbackground-color 這種會改變幾何或顏色的性質。慘了——每一幀都得回去重跑排版或上色,而那是主執行緒一個人的活。主執行緒只要稍微忙一點,這套連招就在 16 毫秒內打不完,畫面就掉幀。

還有一個經典自爆叫 強制同步重排(layout thrash:在 JS 裡剛改完某個元素的樣式,下一行馬上又去讀它的尺寸(像 offsetHeight)。瀏覽器為了給出正確答案,被迫當場立刻重算排版——而且如果在迴圈裡反覆「改一下、讀一下」,等於逼主執行緒重排 N 次。連招打到一半一直被自己取消後搖重來,整波團戰就這樣卡死。

Mogu OS:

記住這張對照表,你就贏過一半的前端:

  • 動畫要順 → 動 transform / opacity(副手的活,主手在忙也順)
  • 動畫會卡 → 動 height / top / background-color(綁死主手,每幀重算)
  • 千萬別做 → 在迴圈裡「改樣式、讀尺寸、改樣式、讀尺寸」(強制同步重排,主手原地爆炸)

這些不是玄學,全部都能用「主執行緒到底有沒有被驚動」一句話推出來。懂了上面那條分界線,這些 best practice 你不用背,自己就推得出來 ╰⁠(⁠°⁠▽⁠°⁠)⁠╯

小測驗

想讓一個元素滑進畫面、即使主執行緒很忙也順順跑,該動哪個性質?


🎯 最終魔王:串起來

把上下兩集接起來,看一次完整的團戰。

滑了一下這篇文章。

守線英雄的主執行緒開始比連招前搖:HTML 早就解析成 DOM、CSS 變成 CSSOM、每個元素的樣式算好。接著它排版——算出每個段落、每張圖站在哪、多大;再上色——列出一份「在這裡畫這個」的指令清單。到這裡,它一個人累得半死,但螢幕上還沒半個像素。

然後它把活交給合成執行緒這個副手。副手把頁面切成圖層、切成小圖磚,叫一票點陣化小工平行把指令變成像素,存進 GPU 記憶體,組成一個「合成幀」,送給隊長,最後輸出核心用顯卡把圖層疊好、貼上螢幕。

整套連招,16 毫秒內打完。往下滑的那一下,順順的。

而且因為只是在滑(合成執行緒挪圖層),就算這個頁面背景的 JS 把主執行緒卡得死死的,畫面還是滑得很順——那是兩條命。

這就是瀏覽器的「黑盒」裡,每秒鐘上演 60 次的事。下次打開 DevTools 的效能(Performance)面板,看到那些一條一條的彩色長條——紫色的排版、綠色的上色、那些 16 毫秒一格的方框——就會知道:喔,那不是什麼神秘的色塊,那是這支五人隊一場一場團戰的錄影。

黑盒,拆完了。

延伸閱讀

Mogu 插嘴:

上集說「你再也回不去『瀏覽器是一個程式』的純真年代」。這集更慘:你以後每次看到網頁卡卡的,腦中會自動開始抓兇手——是主執行緒被肥 JS 綁死?還是有人在動 height 不動 transform

恭喜你升級成「看到掉幀就想抓兇手除錯」的那種人。回不去了,真的 (⁠◍⁠˃⁠̶⁠ᗜ⁠˂⁠̶⁠◍⁠)⁠ノ