什么是 JavaScript 运行时
浏览器之外,JavaScript 需要一个"宿主环境"才能运行——这个环境就是运行时(Runtime)。运行时提供了 JavaScript 引擎之外的一切:文件系统访问、网络 I/O、进程管理、定时器等系统级 API。
- JavaScript 引擎 负责解析、编译、执行 JavaScript 代码的核心组件。Node.js 使用 Google 的 V8,Bun 使用苹果的 JavaScriptCore(JSC)。引擎将 JS 编译为机器码执行。
- 事件循环 单线程处理异步任务的机制。主线程不会阻塞等待 I/O,而是将回调注册到队列,I/O 完成后由事件循环调度执行。
- libuv Node.js 的跨平台异步 I/O 库,提供线程池处理文件 I/O、DNS 等阻塞操作,使 Node.js 的事件循环得以实现。
- JavaScriptCore Apple 为 WebKit 浏览器引擎开发的 JS 引擎,同时用于 Safari 和 iOS 所有浏览器(规定必须使用 JSC)。Bun 选择 JSC 而非 V8,主要原因是 JSC 启动更快、内存占用更低。
Node.js 架构深度解析
Node.js 由 Ryan Dahl 于 2009 年创建,其核心设计思想是:用非阻塞 I/O 和事件驱动模型解决 C10K(同时处理 10000 个连接)问题。
事件循环的六个阶段
Node.js 的事件循环基于 libuv,每次"tick"按顺序经过以下阶段:
┌─────────────────────────────────────────────────────┐
│ 事件循环 (Event Loop) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ timers │→ │ pending │→ │ idle │ │
│ │setTimeout│ │callbacks │ │ prepare │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ ↓ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ close │← │ check │← │ poll │ │
│ │callbacks │ │setImmed. │ │ I/O等待 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 每个阶段之间:清空 nextTick 队列 和 微任务(Promise)队列 │
└─────────────────────────────────────────────────────┘
// 执行顺序示例
console.log('1 同步代码');
setTimeout(() => console.log('4 timers 阶段'), 0);
Promise.resolve().then(() => console.log('3 微任务队列'));
process.nextTick(() => console.log('2 nextTick (优先级最高)'));
// 输出顺序: 1 → 2 → 3 → 4
process.nextTick vs Promise.then:两者都在当前操作完成后、下一事件循环阶段前执行(微任务),但 nextTick 的优先级更高。滥用 nextTick 可能会导致 I/O 饿死——如果 nextTick 不断递归调用自身,poll 阶段永远无法执行。
libuv 线程池
虽然 Node.js 的 JavaScript 是单线程的,但 libuv 维护了一个默认大小为 4 的线程池,用于处理:
- 文件系统操作(
fs.readFile等) - DNS 解析(
dns.lookup) - 部分加密操作(
crypto模块)
# 可通过环境变量调整线程池大小(最大 1024)
UV_THREADPOOL_SIZE=16 node server.js
Bun 的架构创新
Bun 由 Jarred Sumner 于 2021 年开始开发,2023 年发布 1.0 正式版。它不只是一个运行时,更是一个完整的 JavaScript 工具链:
- JavaScript 运行时(JSC 引擎)
- 包管理器(替代 npm/yarn/pnpm)
- 打包器(替代 webpack/esbuild)
- 测试运行器(替代 Jest/Vitest)
- TypeScript/JSX 原生转译
- 运行时(V8 引擎)
- npm / pnpm / yarn(独立安装)
- webpack / esbuild(独立安装)
- Jest / Vitest(独立安装)
- ts-node / tsx / esbuild(独立)
为什么选 JavaScriptCore 而非 V8
这是 Bun 最具争议的设计决策之一。JSC 相比 V8 的优势:
| 指标 | V8(Node.js) | JSC(Bun) |
|---|---|---|
| 冷启动时间 | 较慢(更多预热优化) | 更快(低延迟优先) |
| 内存占用 | 较高 | 更低 |
| 长期峰值性能 | 极高(JIT 充分预热后) | 略低于 V8 峰值 |
| 适合场景 | 长时间运行的服务 | 短命令/边缘计算/开发工具 |
性能对比基准数据
以下数据来自 Bun 官方基准测试(2024 年,Apple M1 Pro),实际生产环境因业务逻辑不同会有差异:
| 场景 | Node.js 22 | Bun 1.x | 提升 |
|---|---|---|---|
| HTTP Hello World(req/s) | ~65,000 | ~120,000 | +84% |
| bun install(冷缓存) | npm ~10s | ~1.5s | 6.7x |
| bun install(热缓存) | npm ~5s | ~180ms | 27x |
脚本启动(--version) | ~70ms | ~5ms | 14x |
| SQLite 读取(100万行) | better-sqlite3 ~0.8s | 内置 ~0.3s | 2.7x |
基准测试的局限性:以上数据是"Hello World"级别的压测。真实业务中,数据库查询、复杂业务逻辑、外部 API 调用等才是性能瓶颈。对于大多数 CRUD 应用,两者性能差异完全感知不到。不要因为 Bun 快就抛弃 Node.js 的成熟生态。
TypeScript 原生支持
这是 Bun 最受开发者喜爱的特性之一——无需任何配置,直接执行 .ts 文件:
# 方案1:ts-node
npm install -D ts-node typescript
npx ts-node server.ts
# 方案2:tsx(推荐)
npm install -D tsx
npx tsx server.ts
# 方案3:Node.js 22.6+ 实验性支持
node --experimental-strip-types server.ts
# 直接运行,无需任何安装
bun run server.ts
# 也支持 JSX/TSX
bun run app.tsx
# 监视模式(类似 nodemon)
bun --watch run server.ts
Bun 的 TS 转译是"类型擦除":Bun 并不做类型检查,只是去掉类型注解执行。这意味着类型错误在运行时不会被发现——你仍然需要在 CI 流程中运行 tsc --noEmit 做类型检查。
Node.js 22+ 新特性
Node.js 并没有停滞不前。22.x LTS(2024 年 10 月起)带来了一系列重要更新:
- V8 12.4:WebAssembly GC、更快的 Array 方法
- 内置
--watch模式:node --watch server.js,无需 nodemon - 实验性 TypeScript 支持:
node --experimental-strip-types require(esm):可以在 CJS 中直接 require ESM 模块- Web Streams API 稳定化:
ReadableStream、WritableStream - 内置测试运行器:
node:test模块完全稳定
// Node.js 22 内置测试(node:test)
import { test, describe } from 'node:test';
import assert from 'node:assert';
test('两数相加', () => {
assert.strictEqual(1 + 1, 2);
});
安装与工具链配置
安装 Node.js(推荐使用版本管理器)
# 方案1:fnm(Fast Node Manager,Rust 编写,推荐)
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 22
fnm use 22
# 方案2:nvm(Node Version Manager,最广泛)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 22
nvm use 22
# 验证
node --version # v22.x.x
npm --version # 10.x.x
安装 Bun
# macOS / Linux
curl -fsSL https://bun.sh/install | bash
# Windows(PowerShell)
irm bun.sh/install.ps1 | iex
# 通过 npm 安装(适合 CI 环境)
npm install -g bun
# 升级 Bun
bun upgrade
# 验证
bun --version # 1.x.x
创建第一个 Bun 项目
# 初始化项目
mkdir my-backend && cd my-backend
bun init
# bun init 会生成:
# ├── index.ts — 入口文件
# ├── package.json — 项目配置
# ├── tsconfig.json — TypeScript 配置
# └── .gitignore
# 运行
bun run index.ts
何时选择 Bun,何时选 Node.js
| 场景 | 推荐 | 理由 |
|---|---|---|
| 全新项目,追求开发体验 | Bun | 原生 TS、极速安装、开箱即用 |
| 成熟产品,稳定性优先 | Node.js | LTS 支持、生态最成熟 |
| Cloudflare Workers / 边缘 | Bun / Hono | 低冷启动,Hono 专为边缘设计 |
| 企业级、合规要求 | Node.js | 经过大量生产验证 |
| 脚本工具 / CLI | Bun | 启动速度快,Shell 内置 |
| 依赖 native addon(.node 文件) | Node.js | Bun 的 Node-API 兼容性仍在完善 |
实用策略:用 bun 作为包管理器和开发运行器(bun install、bun run dev),用 node 作为生产运行器。这样既享受 Bun 的开发速度,又保留 Node.js 的生产稳定性。很多项目正在采用这种混合策略。