3、WebKit 核心架构总览:WebCore 与 JavaScriptCore、渲染引擎与 JS 引擎的分工、多进程架构

聊到 WebKit,很多人第一反应是「浏览器内核」。但说实话,这个说法太笼统了。我做了这么多年 Android 系统开发,见过不少同事把 WebKit 当成一个黑盒来用,出了问题就抓瞎。今天咱们就把这个黑盒拆开,看看里面到底是怎么运作的。

WebKit 的核心架构,说白了就是两大引擎 + 多进程分工。两大引擎分别是 WebCoreJavaScriptCore。多进程架构则是 Browser、GPU、Renderer 三个进程各司其职。咱们一个一个说。

3.1 WebCore 与 JavaScriptCore:双核驱动

WebCore 是 WebKit 的渲染引擎,负责解析 HTML、CSS,构建 DOM 树和渲染树,最终把页面画出来。JavaScriptCore 呢,就是 JS 引擎,负责执行 JavaScript 代码。这两个东西是独立工作的,但必须紧密配合。

我刚开始接触 WebKit 时,有个误区:以为 JS 引擎是渲染引擎的一部分。其实不是。它们俩是平级的,通过绑定层(Binding Layer)来通信。你可以把 WebCore 想象成工厂的生产线,JavaScriptCore 是生产线上的机器人。机器人(JS)可以指挥生产线(WebCore)干活,但生产线本身有自己的节奏。

核心要点: WebCore 管「画」,JavaScriptCore 管「算」。两者通过绑定 API 交互,互不阻塞。

为什么会这样设计?你想想看,如果 JS 引擎和渲染引擎耦合在一起,那执行一段耗时的 JS 代码时,整个页面就卡死了。分开之后,JS 引擎可以异步执行,渲染引擎该画就画,互不影响。当然,实际上还有更复杂的同步机制,但大原则就是这样。

3.2 渲染引擎与 JS 引擎的分工

咱们来细化一下分工。WebCore 内部有几个关键模块:

  • HTML 解析器: 把 HTML 文本解析成 DOM 树
  • CSS 解析器: 解析 CSS,生成样式规则
  • 布局引擎: 计算每个元素的位置和大小
  • 绘制引擎: 把布局结果绘制到屏幕上

JavaScriptCore 呢,主要负责:

  • 词法/语法分析: 把 JS 源码变成抽象语法树(AST)
  • 即时编译(JIT): 把热点代码编译成机器码,提升执行速度
  • 垃圾回收: 自动管理内存,回收不再使用的对象

这里有个关键点:DOM 操作。JS 代码经常要操作 DOM,比如 document.getElementById。这个操作其实是 JS 引擎通过绑定层调用 WebCore 的接口。每次 DOM 操作都有一定的开销,所以我在项目中一直强调:减少 DOM 操作,批量更新。

避坑指南: 我曾经在一个性能优化项目中,发现页面卡顿的原因是 JS 频繁读取 offsetHeight。每次读取都会触发强制回流(Forced Reflow),WebCore 不得不重新计算布局。后来改成缓存这些值,性能提升很明显。

为了更直观地理解,我画了一张架构图:

WebKit 核心架构总览 WebCore(渲染引擎) HTML 解析器 CSS 解析器 布局引擎 绘制引擎 DOM 树 / 渲染树 绑定层 (Bridge) JavaScriptCore(JS 引擎) 词法/语法分析 JIT 编译器 字节码解释器 垃圾回收器 执行栈 / 堆内存 WebCore 与 JavaScriptCore 通过绑定层通信,实现 DOM 操作与渲染的协作

3.3 多进程架构:Browser / GPU / Renderer

早期的浏览器是单进程的,一个页面崩溃整个浏览器都挂了。后来 Chrome 率先引入了多进程架构,WebKit 也跟进。在 Android 平台上,WebView 默认也是多进程模式。

三个核心进程的分工如下:

进程 职责 典型问题
Browser Process 管理 UI、地址栏、网络请求、文件访问 OOM(内存不足)时容易崩溃
Renderer Process 每个标签页一个,负责解析、渲染、执行 JS 沙箱隔离,崩溃不影响其他进程
GPU Process 负责图形合成、硬件加速 驱动兼容性问题,黑屏

这里有个细节:Renderer Process 是运行在沙箱里的。它不能直接访问文件系统或网络,必须通过 Browser Process 转发。这样做是为了安全——即使某个页面被攻破,攻击者也拿不到系统权限。

注意: 在 Android 上,WebView 的多进程模式是通过 android:process 属性配置的。如果你在应用中同时使用多个 WebView,记得考虑进程间通信(IPC)的开销。我曾经遇到过一个案例,多个 WebView 同时加载大页面,导致 Browser Process 的 Binder 线程池耗尽,页面加载超时。

进程间通信主要靠 IPC(Inter-Process Communication)。WebKit 使用 Chromium 的 IPC 框架,基于 Mojo 协议。Renderer Process 把绘制指令序列化,通过 IPC 发给 GPU Process,GPU Process 再调用 OpenGL ES 或 Vulkan 进行渲染。

你可能会问:为什么不直接在 Renderer Process 里渲染?原因有两个:一是安全,二是稳定性。GPU 驱动经常有 bug,如果 Renderer Process 直接调用 GPU,一个驱动崩溃就能拖垮整个标签页。通过 GPU Process 隔离,驱动崩溃只影响合成,Renderer Process 可以重新发起请求。

个人经验: 我在做系统级 WebView 优化时,发现 GPU Process 的内存占用经常居高不下。后来定位到是纹理缓存没有及时释放。解决方案是监听 onTrimMemory() 回调,在内存紧张时主动清理 GPU 缓存。这个优化让低端机的页面滚动流畅度提升了 30% 以上。

最后,用一张图总结多进程架构:

WebKit 多进程架构 Browser Process UI 管理 网络请求 文件访问 存储管理 Renderer Process HTML/CSS 解析 JS 执行 布局计算 绘制指令生成 GPU Process 图形合成 纹理管理 硬件加速 驱动调用 IPC IPC 每个标签页对应一个 Renderer Process,通过 IPC 与 Browser 和 GPU 进程通信

嗯,以上就是 WebKit 核心架构的全貌。WebCore 和 JavaScriptCore 各司其职,多进程架构保障了稳定性和安全性。理解这些基础,后面咱们聊渲染流程、JS 绑定、性能优化时,你就能知道每个环节到底发生在哪个进程、哪个模块里了。


公众号:蓝海资料掘金营,微信deep3321