1327 字
7 分钟
Vite 与 Node.js 运行时基础

Vite 在开发环境的表现#

Vite 开发环境不预先打包整个应用。它以原生 ESM 的形式按模块提供源码,浏览器请求到哪个文件,Vite 才编译哪个文件,冷启动时间不随项目规模线性增长。

冷启动指应用从完全关闭状态到服务可用的过程,包含依赖分析、编译和启动开发服务器。热更新(HMR)指运行期间替换、增删模块,页面不刷新、服务不重启。

生产构建用 Rolldown 和 Oxc#

Vite 8 默认使用 Rolldown 和 Oxc,取代了早期的 esbuild 和 Rollup。Rolldown 负责打包,依赖预构建也不再走 esbuild。esbuild 现在只是一个可选依赖。

这套工具保留了 Rollup 兼容的插件 API。大多数 Rollup 插件可以直接用;用到 Rolldown 尚未支持的选项时仍需调整。

Node.js 的运行时分层#

Node.js 是四层结构:V8 引擎、libuv、内置绑定层和标准库。JS 代码在 V8 里执行,异步 I/O 交给 libuv,两者由绑定层连接,上层用标准库提供的 API。

  • V8:编译并执行 JavaScript,JIT 把热点代码转成机器码,同时管理堆内存、调用栈和垃圾回收。它不提供文件、网络能力,只执行语言本身。
  • libuv:C 写的跨平台异步 I/O 库,实现事件循环、线程池以及文件、网络、定时器支持。fs、net、http、dns 的异步操作最终都落到这里。
  • 内置绑定层:C/C++ 能力与 JS 之间的桥,让 JS 能调用文件、网络、信号、进程等系统调用。
  • 标准库:fs、path、http、stream、events、buffer 这些 JS 模块,直接给上层开发者使用。

事件循环的六个阶段#

事件循环按阶段反复执行。Node.js 文档给出的六阶段示意图是简化版,下面只列常见的六个。

  • timers:执行到期的 setTimeout 和 setInterval 回调
  • pending callbacks:处理上一轮遗留的系统级回调,比如 TCP 错误
  • idle、prepare:libuv 内部使用
  • poll:等待新的 I/O 事件,执行绝大多数 I/O 回调,也是耗时最长的阶段
  • check:执行 setImmediate 回调
  • close callbacks:执行 close 事件,比如 socket.destroy

微任务与 process.nextTick#

一轮任务结束后会清空微任务队列,这就是“微任务插在任务之间”的含义。nextTick 队列和 Promise 微任务队列各清一次。

默认顺序是 nextTick 队列排在 Promise 微任务队列前面。ESM 里有一个例外:模块求值本身属于微任务队列。

所以在模块顶层同时用这两个 API 时,queueMicrotask() 的回调先执行,process.nextTick() 的回调后执行。

Node.js 文档把 process.nextTick 标为 Stability 3(Legacy),并建议新代码改用 queueMicrotask()。

单线程模型与 I/O 线程池#

Node.js 默认只有一个执行 JavaScript 的线程,JS 代码在其中顺序执行,不并行。

需要并行执行 JavaScript 时用 node:worker_threads,每个工作线程有自己的事件循环和 V8 实例。

I/O 不在 JS 线程里执行。文件 I/O、DNS 解析(getaddrinfo、getnameinfo)和通过 uv_queue_work() 提交的任务会进入 libuv 线程池。

线程池默认 4 个线程,可用 UV_THREADPOOL_SIZE 调整,上限 1024。

网络 I/O 不走线程池,它始终由事件循环所在的那个线程处理。

单线程的收益是不需要加锁,也没有线程竞争。代价是纯 CPU 计算会占满主线程,事件循环停住,请求只能排队等待。

两套模块规范#

CommonJS 用 require() 和 module.exports 同步加载,依赖在运行时解析。加载器没法可靠地静态分析出用到了哪些导出,tree-shaking 因此失效。

浏览器也不原生支持 CommonJS,必须先由打包器翻译。

ESM 是语言标准,用 import 和 export,依赖关系在编译期就能确定。它支持 tree-shaking、顶层 await 和异步加载,浏览器原生支持。

Node.js 现在同时支持这两套规范:CommonJS 在 Node.js 文档里是 Stability 2(Stable),也是 Node 最早的模块格式;ESM 是 ECMAScript 语言标准。

V8 的内存管理与垃圾回收#

V8 负责内存管理,回收按对象年龄分代处理。新生代对象用 Scavenger 回收,活下来的对象晋升到老生代。

老生代先跑标记清除(Mark-Sweep)找出垃圾,再跑标记整理(Mark-Compact)压缩内存,减少碎片。

要避开的是让对象一直可达的写法:没清理的定时器和事件监听、只增不减的全局缓存、闭包长期持有的大对象。这些对象不会被回收,内存占用会一直上涨。

第 17 篇,共 19 篇本文属于系列

Vite 与 Node.js 运行时基础
https://www.choria.top/posts/vue3-configuration/
作者
乔瑞雅·克林威尔
发布于
2026-04-08
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 214 天

部分内容可能已过时