Bun 与 Deno 运行时
这页适合作为“Node.js 之外现代 JavaScript 运行时的入门比较”。Bun 和 Deno 都很强,但它们适合解决的问题并不完全一样;先按项目目标选,比单纯追新更重要。
先按场景选
- 想在 Node 生态里提速:优先 Bun
- 想一套工具同时管运行、测试、打包:优先 Bun
- 想要默认权限隔离与更强约束:优先 Deno
- 想要标准 Web API 风格和内置工具链:优先 Deno
- 已有大型 Node 项目要稳妥迁移:先小范围尝试,不要整仓库直接切
Bun
Bun 是用 Zig 编写的高性能 JavaScript 运行时,集成了包管理器、打包器和测试运行器。
安装
# Windows
scoop install bun
# 或
powershell -c "irm bun.sh/install.ps1 | iex"
# macOS/Linux
curl -fsSL https://bun.sh/install | bash
运行脚本
bun run index.ts # 直接运行 TypeScript
bun run dev # 运行 package.json scripts
bun --watch index.ts # 文件变更自动重启
包管理
bun init # 初始化项目
bun add express # 添加依赖
bun add -d vitest # 添加开发依赖
bun remove express # 移除
bun install # 安装所有依赖
bun update # 更新依赖
Bun 使用 bun.lock(二进制格式),速度远快于 npm/pnpm。
Bun 更适合什么
- 前端工具链
- 本地脚本和 CLI
- 追求安装速度、测试速度和开发反馈速度的项目
- 与 Node.js 兼容度要求高,但想减少工具碎片化的团队
内置 API
// 文件操作
const file = Bun.file("data.json");
const text = await file.text();
await Bun.write("output.txt", "hello");
// HTTP 服务器
Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello Bun!");
},
});
// 密码哈希
const hash = await Bun.password.hash("mypassword");
const valid = await Bun.password.verify("mypassword", hash);
// Shell
import { $ } from "bun";
const result = await $`ls -la`.text();
测试
bun test
bun test --watch
// test.ts
import { expect, test } from "bun:test";
test("2 + 2", () => {
expect(2 + 2).toBe(4);
});
打包
bun build ./src/index.ts --outdir ./dist --target browser
bun build ./src/index.ts --outdir ./dist --target node --minify
Deno
Deno 是 Node.js 创始人 Ryan Dahl 的新运行时,默认安全、原生 TypeScript。
安装
# Windows
scoop install deno
# 或
winget install DenoLand.Deno
# macOS/Linux
curl -fsSL https://deno.land/install.sh | sh
运行
deno run index.ts # 运行(无权限)
deno run --allow-net index.ts # 允许网络
deno run --allow-read --allow-write index.ts
deno run -A index.ts # 允许所有权限
deno task dev # 运行 deno.json 中的 task
权限模型
Deno 默认不允许任何系统访问:
| 权限 | 说明 |
|---|---|
--allow-net | 网络访问 |
--allow-read | 文件读取 |
--allow-write | 文件写入 |
--allow-env | 环境变量 |
--allow-run | 运行子进程 |
--allow-ffi | 外部函数接口 |
-A | 所有权限 |
包管理
// deno.json
{
"imports": {
"oak": "jsr:@oak/oak@^17",
"std/": "jsr:@std/",
},
"tasks": {
"dev": "deno run --watch -A main.ts",
"build": "deno compile -A main.ts",
},
}
deno add jsr:@oak/oak # 添加依赖
deno install # 安装依赖
Deno 2.x 也支持 npm: 前缀直接使用 npm 包:
import express from "npm:express";
Deno 更适合什么
- 对权限边界敏感的脚本和服务
- 希望默认集成
fmt、lint、test、compile的项目 - 偏标准 Web API 和现代运行时体验的团队
- 想把单文件工具直接编译成可执行文件的场景
内置工具
deno fmt # 格式化
deno lint # Lint
deno test # 测试
deno bench # 基准测试
deno compile main.ts # 编译为单文件可执行
deno doc mod.ts # 生成文档
对比
| 特性 | Node.js | Bun | Deno |
|---|---|---|---|
| 语言 | C++ | Zig | Rust |
| TypeScript | 需要编译 | 原生支持 | 原生支持 |
| 包管理 | npm/pnpm/yarn | bun(内置) | deno add / npm |
| 安全模型 | 无限制 | 无限制 | 默认沙箱 |
| 兼容性 | 生态最大 | 高度兼容 Node | 兼容 Node/npm |
| 速度 | 基准 | 最快 | 快 |
推荐落地顺序
建议不要一开始就“主项目整体迁移”。更稳的做法是:
- 先拿小脚本或新建工具项目试运行时
- 再验证依赖兼容性、测试工具和构建流程
- 再决定是否扩大到服务或前端项目
- 最后才考虑作为团队默认运行时
常见问题
Bun 能不能完全替代 Node.js
很多项目可以部分替代,但不代表所有项目都该马上切。重点要看:
- 依赖兼容性
- CI/CD 环境
- 原生模块需求
- 团队对 Node 工具链的绑定程度
Deno 默认权限很烦吗
刚开始会有一点约束感,但这正是它的价值之一。对脚本、自动化和边界敏感场景来说,这种显式授权反而更清晰。
一个仓库里能不能同时有 Node、Bun、Deno
可以,但要有明确边界。比如主应用仍用 Node,单独工具脚本用 Bun 或 Deno。最怕的是没人知道哪个命令该用哪个运行时。
风险提醒
- 不要为了“新”而迁移成熟项目
- 锁文件和 CI 运行时要统一
- 运行时切换前先验证第三方依赖、测试和部署链路