JavaScript 的宿主环境:浏览器 vs Node.js

7 阅读2分钟

1. 宏观进程架构

浏览器(以 Chrome 为例)—— 多进程架构

  • Browser 进程(主进程):负责地址栏、书签、前进/后退,以及网络请求、文件访问等特权操作
  • Renderer 进程:每个 Tab(或同站点组)一个,负责页面渲染、JS 执行、DOM 操作。出于安全考虑运行在沙箱
  • GPU 进程:统一处理合成、绘制
  • Plugin 进程:运行浏览器插件
  • Utility 进程:网络服务、音视频解码等

Node.js —— 单进程、单主线程模型

  • 一个主线程运行 JS(V8 引擎)
  • 通过 libuv 线程池处理文件 I/O、DNS 查询等阻塞操作
  • 可通过 worker_threads 创建工作线程,或 child_process / cluster 创建子进程

2. 共同的核心:V8 引擎

无论是浏览器还是 Node.js,JS 代码的解析、编译、执行都依赖 V8 引擎。V8 负责的事情是纯粹的:

  • 将 JS 源码解析为 AST
  • 通过 Ignition 解释器生成字节码
  • 通过 TurboFan 将热点代码优化编译为机器码
  • 管理堆内存、垃圾回收(GC)

V8 本身不提供任何 I/O 能力——它不知道什么是 DOM、什么是文件系统、什么是网络请求。这些能力由宿主环境注入。

3. 宿主环境的差异

能力浏览器Node.js
DOM / BOMdocument, window, navigator
文件系统无(有限的 File API)fs, path 等模块
网络fetch, XMLHttpRequesthttp, net, dgram 等模块
进程/线程Web Workerschild_process, worker_threads
事件循环实现浏览器内核实现(与渲染管线交织)libuv 实现
模块系统ES Modules(原生)CommonJS + ES Modules
全局对象window / globalThisglobal / globalThis

4. 浏览器中 JS 的执行模型

浏览器中的 Renderer 进程,核心就是两件事的协作:页面渲染事件循环

  • 页面渲染:HTML 解析 -> DOM 树 + CSSOM -> Layout -> Paint -> Composite。JS 可以在任意阶段介入修改 DOM/CSSOM,触发重排(reflow)或重绘(repaint)。
  • 事件循环:JS 的主线程通过事件循环调度任务。一轮循环大致为:
    1. 宏任务队列(macrotask)取一个任务执行(如 setTimeout 回调、用户事件)
    2. 清空微任务队列(microtask)(如 Promise.thenMutationObserver
    3. 如果需要,执行渲染更新(requestAnimationFrame -> Style -> Layout -> Paint)
    4. 回到第 1 步

关键点在于:JS 执行和页面渲染共享同一个主线程,所以长时间的 JS 运算会阻塞渲染,造成页面卡顿。

总结

V8 是 JS 的"发动机",浏览器和 Node.js 是不同的"车身"——发动机相同,但车身提供的能力(DOM vs 文件系统)、调度方式(浏览器事件循环 vs libuv 事件循环)各不相同。