浏览器有两套引擎:一套渲染 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,以及按苹果规则几乎所有的 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。 苹果的组合,也是 iOS 浏览器逃不开的那一套。JavaScriptCore 分四层递进执行——先是 LLInt 解释器,再依次是 Baseline、DFG、FTL 这三级 JIT 编译器——每一层都在一个函数证明自己值得优化之后,用更多编译时间换取更高的执行速度。这四层跑的是同一套字节码格式,WebKit 在 2019 年重写了它,把内存占用砍掉了大约一半。
由于苹果要求 iOS 浏览器必须使用 WebKit,「iPhone 上的 Chrome」其实是把 Blink 的界面习惯套在 WebKit 和 JavaScriptCore 之上——而不是 V8。它只是「感觉」像 Chrome,底层这两套引擎其实都是苹果自己的。自 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——苹果自家的引擎,与 WebKit 渲染引擎搭配使用。由于苹果要求 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 的浏览器内核检测,看看你的浏览器实际运行的到底是哪一对引擎。
推荐阅读:


