瀏覽器其實有兩套引擎:一套算繪 HTML/CSS,另一套執行 JavaScript。看 Blink、Gecko、WebKit 各自如何搭配 JS 引擎。
「瀏覽器核心」是網頁開發中最容易被隨口用錯的術語之一,因為瀏覽器其實並不只有一個引擎,而是有兩個,各自負責完全不同的工作。算繪引擎把 HTML 與 CSS 轉化為你螢幕上的像素;JavaScript 引擎執行讓頁面產生互動的指令碼。兩者並肩運作,由同一家廠商打造,人們卻常常把它們混為一談。它們並不是同一回事——這個區別不只是術語層面的較真:JavaScript 引擎有它自己的身分訊號,與算繪引擎所揭露的資訊完全獨立。
重點摘要
- 瀏覽器把負責解析 HTML/CSS 並繪製像素的算繪(排版)引擎,和負責執行指令碼的獨立 JavaScript 引擎搭配在一起——它們是兩套不同的軟體。
- 真正的三對組合是:Blink + V8(Chrome、Edge 以及其他 Chromium 瀏覽器)、Gecko + SpiderMonkey(Firefox)、WebKit + JavaScriptCore(Safari,以及依 Apple 規則運作的幾乎所有 iOS 瀏覽器)。
- 一次頁面載入會讓兩套引擎協同運作:算繪引擎的解析 → 樣式 → 版面配置 → 繪製流程,與 JavaScript 引擎執行指令碼並即時改動這條流程同步進行。
- JavaScript 引擎有自己可被偵測的指紋——浮點數捨入方式、錯誤訊息措辭、
TypedArray行為、JIT 暖機耗時——這些都獨立於一旁搭配的是哪種算繪引擎。 - BrowserInsight 的瀏覽器核心檢測會同時檢查這兩部分,因為一次以假亂真的偽裝,既要騙過算繪引擎的行為,也要騙過 JavaScript 引擎的行為,才夠可信。
一個瀏覽器,兩套引擎
打開任何一個瀏覽器,幕後其實是兩套獨立的軟體在協作,才把頁面呈現給你看。算繪引擎(有時也叫排版引擎)讀取頁面的 HTML 與 CSS,判斷要畫出什麼;JavaScript 引擎讀取並執行頁面的 <script> 程式碼——運算、DOM 操作、事件處理,一切動態的部分。
它們是真正獨立的程式碼庫,各自獨立開發、獨立發版,即便同屬一個瀏覽器專案也不例外。算繪引擎完全不懂如何執行 JavaScript;JavaScript 引擎也完全不知道如何排版一個 <div>。把它們連接起來的是一層綁定程式碼,讓 JavaScript 能夠呼叫算繪引擎所維護的 DOM——這也是為什麼「JavaScript 引擎改動了頁面」和「算繪引擎繪製了頁面」是兩個獨立的事件,而不是同一件事。
我們的瀏覽器核心指南已經深入講解了 Blink、Gecko、WebKit 這三種算繪引擎。這篇文章要談的,是那篇指南只是輕輕帶過的另一半:緊挨著它們運作的 JavaScript 引擎。
真正的三對組合
市面上的瀏覽器名字有幾十個,但真正在用的算繪引擎與 JavaScript 引擎各自只有三種,每家廠商都各自配好了一對:
| 瀏覽器 | 算繪引擎 | JavaScript 引擎 |
|---|---|---|
| Chrome、Edge、Opera、Brave 以及其他 Chromium 瀏覽器 | Blink | V8 |
| Firefox | Gecko | SpiderMonkey |
| Safari,以及 iOS/iPadOS 上幾乎所有的瀏覽器 | WebKit | JavaScriptCore(也叫 Nitro) |
Blink + V8。 兩者皆由 Google 維護。V8 透過多層即時編譯(JIT)把 JavaScript 編譯成機器碼,在它逐漸摸清哪些函式是「熱點」的過程中,用編譯時間換取執行速度。Blink 把 <script> 內容交給 V8,並向 V8 的執行環境暴露它可以呼叫的 DOM 物件。
Gecko + SpiderMonkey。 Mozilla 的組合,從 Netscape 時代起就一直配對開發。SpiderMonkey 的「Warp」JIT 大改造隨 Firefox 83 推出,重構了它的分層機制,降低了記憶體用量、加快了真實頁面的執行速度——很好地說明了 JavaScript 引擎可以獨立於同期任何算繪引擎的變化而自行演進。
WebKit + JavaScriptCore。 Apple 的組合,也是 iOS 瀏覽器逃不掉的那一套。JavaScriptCore 分四層遞進執行——先是 LLInt 直譯器,接著依序是 Baseline、DFG、FTL 這三級 JIT 編譯器——每一層都在一個函式證明自己值得最佳化之後,用更多編譯時間換取更高的執行速度。這四層跑的是同一套位元組碼格式,WebKit 在 2019 年重寫了它,把記憶體用量砍掉了大約一半。
由於 Apple 要求 iOS 瀏覽器必須使用 WebKit,「iPhone 上的 Chrome」其實是把 Blink 的介面習慣套在 WebKit 與 JavaScriptCore 之上——而不是 V8。它只是「感覺」像 Chrome,底層這兩套引擎其實都是 Apple 自家的。自 iOS 17.4 起,歐盟《數位市場法》已經開始鬆動這條規則,但現實中的 iOS 瀏覽器仍清一色是 WebKit——歐盟境內境外皆然。
在一次頁面載入中看兩套引擎協作
一次完整的頁面載入會讓這個分工變得非常具體。當瀏覽器抓取一個頁面時,大致會發生這些事:
- 解析 —— 算繪引擎讀取原始 HTML,建立出 DOM 樹;讀取 CSS,建立出樣式樹。
- 指令碼開始執行 —— 一旦解析引擎遇到
<script>標籤(或某個已解析、被延後執行的指令碼觸發),JavaScript 引擎就會接手,執行程式碼去讀取並改寫算繪引擎剛剛建立出來的 DOM。 - 樣式與版面配置 —— 算繪引擎把 DOM 與 CSS 合併成算繪樹,並計算每個方塊的位置與大小,同時把 JavaScript 所做的任何改動都納入考量。
- 繪製 —— 算繪引擎把這份版面配置轉化為實際的像素。
如果某段指令碼在初次繪製之後又修改了 DOM——附加一個元素、改變一個類別、給樣式加動畫——第 3 步與第 4 步就會針對受影響的區域重新執行一次。這個循環,也就是 JavaScript 引擎與算繪引擎來回交接控制權,會在頁面存在期間不斷重複。兩套引擎都做不了對方的工作,但只要有一個慢下來,整個循環就會被拖累:算繪引擎畫不出一個還沒算完的、卡住的 JavaScript 引擎的計算結果;而如果算繪引擎排版輸出的速度跟不上,再快的 JavaScript 引擎也是浪費。
為什麼這個分工對身分辨識與偵測很重要
這不只是架構層面的一個註腳——它正是瀏覽器辨識必須同時檢查兩件事,而不是只查一件的原因。
算繪引擎會透過繪製方式留下行為痕跡:字型算繪、CSS 特性支援、版面配置的取捨。JavaScript 引擎則會透過運算方式留下完全獨立的一套痕跡:邊界情況下的浮點數捨入、內建錯誤訊息的具體措辭(V8、SpiderMonkey、JavaScriptCore 拋出的 TypeError 文字細節各不相同)、TypedArray 與 Array 方法在邊界情況下的行為,以及 JIT 暖機耗時——一個熱點函式需要被呼叫多少次才會被最佳化。這些都跟版面配置毫無關係。
舉一個你在任何主控台裡都能驗證的例子:new Error().stack。V8 會先把錯誤的名稱與訊息放在字串開頭,每一個堆疊框寫成 at fnName (url:line:column);SpiderMonkey 與 JavaScriptCore 都沒有這個開頭,堆疊框的寫法是 fnName@url:line:column。同一種語言、同一份標準,卻有三種不同的字串樣貌——完全取決於正在執行程式碼的是哪一個 JavaScript 引擎,而且任何指令碼一問便知。
正是這種獨立性,讓 JavaScript 引擎訊號在識破偽裝時格外有價值。一段指令碼可以偽造 user-agent 字串,或者覆寫 navigator 的屬性來冒充另一款瀏覽器,改變的只是頁面聲稱的身分——但它改變不了底層實際執行程式碼的到底是哪一個 JavaScript 引擎。如果一個頁面聲稱自己是 iOS 上的 Safari,但其 JavaScript 引擎的錯誤訊息措辭與浮點數行為卻與 V8 相符、而非 JavaScriptCore,這就是一個指令碼可以直接測出來的真實矛盾,跟算繪完全無關。
這正是 BrowserInsight 的瀏覽器核心檢測不止步於算繪引擎偵測的原因——它同樣會探測 JavaScript 引擎的行為。一次可信的偽裝必須讓瀏覽器的兩半都保持一致,而這遠比改一個字串困難得多。
常見問題
Blink 就是 V8 嗎?
不是。Blink 是 Google 的算繪引擎——負責解析 HTML/CSS 並繪製頁面;V8 是 Google 另一套獨立的 JavaScript 引擎——負責執行指令碼。兩者都內建在 Chrome 及其他 Chromium 瀏覽器中,也都由 Google 打造,但它們是各自獨立、職責不同的程式碼庫。
Firefox 和 Chrome 用的是同一個 JavaScript 引擎嗎?
不是。Firefox 把 Gecko(算繪)與 SpiderMonkey(JavaScript)搭配在一起,後者是 Mozilla 自家的引擎,跟 Google 的 V8 沒有關係。Chrome、Edge、Brave 以及其他 Chromium 瀏覽器用的都是 V8。
Safari 用的是什麼 JavaScript 引擎?
JavaScriptCore,也叫 Nitro——Apple 自家的引擎,與 WebKit 算繪引擎搭配使用。由於 Apple 要求 iOS 瀏覽器必須使用 WebKit,iPhone 上的瀏覽器也都在執行 JavaScriptCore,不管它自稱是哪款瀏覽器。自 iOS 17.4 起,歐盟《數位市場法》已允許在 iOS 上使用其他引擎,但現實中幾乎每一款 iOS 瀏覽器仍跑在 WebKit 與 JavaScriptCore 上。
兩個瀏覽器能不能共用同一個算繪引擎,卻用不同的 JavaScript 引擎?
在目前的主流瀏覽器裡沒有這種情況——每家廠商都把算繪引擎與 JavaScript 引擎作為固定配對來發布(Blink+V8、Gecko+SpiderMonkey、WebKit+JavaScriptCore)。由於這兩套引擎本來就是各自獨立的專案,技術上把它們混搭起來並非不可能,只是目前沒有一款主流瀏覽器這麼做。
結語
瀏覽器引擎和 JavaScript 引擎不是同一回事,儘管打造它們的往往是同一小撮廠商,瀏覽器也常常只被一個名字統稱。Blink 配 V8,Gecko 配 SpiderMonkey,WebKit 配 JavaScriptCore——三對真正獨立的組合,做著兩件真正獨立的工作,在每一次頁面載入中協同運作。由於 JavaScript 引擎會留下自己獨有的行為痕跡,跟算繪引擎揭露的資訊毫不重疊,一次真正可靠的「這是什麼瀏覽器」檢測,必須同時看這兩部分。執行 BrowserInsight 的瀏覽器核心檢測,看看你的瀏覽器實際運行的到底是哪一對引擎。
推薦閱讀:


