程序员茄子的 2026 深度复盘:Bun 放弃 Zig 全面 Rust 化、Deno 2.9 杀入桌面——JS 运行时大战进入新纪元
前言:两个同源项目,走上了完全不同的进化路径
2026 年的 JavaScript 运行时战场,比以往任何时候都热闹。
一边是 Bun——JavaScript 世界里增长最快的"搅局者",在被 Anthropic 收购两个月后,创始人 Jarred Sumner 用 Claude Fable 5 模型辅助,耗时 11 天写下了超过 100 万行 Rust 代码,将运行时的核心从 Zig 完全迁移到 Rust,发布 Bun v1.4.0(Canary),声称可靠性大幅提升。
另一边是 Deno——Node.js 创始人 Ryan Dahl 的"理想主义续作",在 Deno 2.9 中正式引入 deno desktop 命令行,将桌面打包作为 Deno 运行时的一个全新输出目标,不再需要独立维护 Electron 风格的 main/preload/renderer 三层工程。
这两个事件,表面上看是两条独立的产品新闻,但深挖进去,它们揭示了同一个底层趋势:JavaScript 运行时的工程范式,正在从"功能集成"转向"架构收敛",而 AI 正在成为这场收敛的核心推手。
本文将从技术架构、工程实践、产业影响三个维度,对这两个重大事件进行深度拆解,并给出对开发者选型的实际建议。
一、Bun 全面 Rust 重写:为什么是现在,为什么是 Rust
1.1 从 Zig 到 Rust:一次被迫的架构迁移
Bun 最早于 2022 年发布,核心定位是"Node.js 的替代品"——更快的启动速度、内置 TypeScript 支持、一体化的打包/测试/包管理工具。Bun 选择 Zig 作为核心语言,原因很直接:Zig 定位为"C 的替代品",语法简洁、内存控制精确、编译产出干净,没有 C++ 的复杂性,也没有 Rust 的陡峭学习曲线。
但现实给了 Sumner 一记重锤。
Zig 的内存安全特性在实际大规模工程中表现并不稳定。根据 Sumner 本人在博客中的描述,Zig 代码"经常出现内存错误和崩溃",而这些错误"很难彻底修复"。这对于一个面向生产环境的 JavaScript 运行时来说,是致命的。
Rust 的设计恰好针对这个痛点:编译器在编译期强制捕获所有内存安全问题,让运行时能够在编译时就消灭掉大量潜在的崩溃。代价是学习曲线陡峭——但这一次,Sumner 没有选择独自面对。
1.2 Claude Fable 5:AI 辅助重构的工程极限
最令人震惊的,不是重构本身,而是重构的速度。
Sumner 使用了 64 个 Claude 实例并行运行,持续 11 天,完成了超过 100 万行代码的迁移。按他的估算,同样的工作量如果交给人工团队,需要大约一年时间。
这里有几个数字值得关注:
| 指标 | 数值 |
|---|---|
| 重写时长 | 11 天 |
| 并行 Claude 实例数 | 64 |
| 代码行数 | > 100 万行 |
| API 成本 | 约 16.5 万美元(约 111.9 万元人民币) |
| 发布版本 | Bun v1.4.0 (Canary) |
| 修复的 bug 数 | 128 个 |
| 速度提升 | 约 2%~5% |
速度提升"只有"2%~5%,这个数字初看让人失望。但我们需要理解这里的核心目标:这次迁移的首要目标是可靠性,不是性能。Rust 的类型系统在编译期捕获的内存问题,在 Zig 版本中可能以运行时崩溃的形式出现,而这类崩溃在服务端场景下代价极高。
2%~5% 的性能提升是附加收益——Zig 的手动内存管理与 Rust 的零成本抽象在性能上本来就在同一量级,差距主要体现在稳定性而非吞吐量。
1.3 Anthropic 收购的影响:从商业到技术的连锁反应
2025 年 12 月,Anthropic 收购了 Bun 及其团队。这个动作的意义远超普通的商业收购。
Claude Code 已经在可执行文件中检测到了 Bun v1.4.0 的存在——换句话说,Anthropic 正在将 Bun 作为 Claude Code 的底层执行引擎。这是一个聪明的策略:Claude Code 需要一个高性能的 JavaScript 运行时来执行 AI 生成的代码片段,而 Bun 的启动速度和 TypeScript 原生支持恰好满足这个需求。
但这次收购也带来了一些值得关注的信号:
信号一:AI 公司开始垂直整合开发者工具链。 Anthropic 收购 Bun 不是为了做云服务,而是为了强化 Claude Code 的本地执行能力。这意味着未来 AI 编程工具与底层运行时的绑定会越来越深。
信号二:Rust 正在成为系统级工具的标准语言。 从 Linux 内核,到 WebAssembly 工具链(wasmtime、wasm-pack),再到 JavaScript 运行时(Bun、Tauri 的底层),Rust 正在构建一个横跨操作系统、嵌入式、WebAssembly 的生态版图。
1.4 Rust 重写背后的技术细节
Bun 的 Rust 重写涉及多个核心子系统,这里我们深入到代码层面,分析几个关键模块的迁移策略:
1.4.1 JavaScriptCore 引擎集成
Bun 仍然使用 JavaScriptCore(JSC)作为 JavaScript 引擎,这与 WebKit/Safari 相同。Zig 和 Rust 都需要通过 FFI 与 C++ 层的 JSC 交互,但处理方式不同:
Zig 版本的 FFI 通信:
const jsc = @import("bun_jsc.zig");
pub fn executeScript(ctx: *JSC.JSContextRef, code: [*]const u8, len: usize) !JSC.JSValueRef {
return jsc.evaluate(ctx, code, len);
}
Rust 版本的 FFI 通信(via autocxx 或手动绑定):
use ffi::jsc::*;
pub fn execute_script(ctx: *mut JSContextRef, code: *const u8, len: usize) -> Result<JSValueRef, JscError> {
unsafe {
jsc_evaluate(ctx, code, len)
}
}
Rust 的优势在于其 FFI 边界可以通过 unsafe 块精确标注,配合 cargo clippy 和 miri 进行内存安全检查,而 Zig 在这一层面的工具链还不够成熟。
1.4.2 HTTP 服务器的 Rust 实现
Bun 内置了一个高性能 HTTP 服务器,这是其相比 Node.js 的核心优势之一。Rust 版本使用 tokio 异步运行时:
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use bytes::Bytes;
pub struct BunHttpServer {
router: Router,
pool: ThreadPool,
}
impl BunHttpServer {
pub async fn listen(&self, addr: &str) -> std::io::Result<()> {
let listener = TcpListener::bind(addr).await?;
loop {
let (socket, _) = listener.accept().await?;
let handler = self.router.clone();
self.pool.execute(async move {
process_connection(socket, handler).await;
});
}
}
}
async fn process_connection(socket: TcpStream, router: Router) -> std::io::Result<()> {
let mut buf = [0u8; 8192];
let (mut reader, mut writer) = socket.split();
let n = reader.read(&mut buf).await?;
let request = parse_request(&buf[..n])?;
let response = router.route(&request).await;
writer.write_all(&response.to_bytes()).await?;
Ok(())
}
这个模式与 Node.js 的 event-loop + worker threads 有本质区别——tokio 的多线程调度让 I/O 操作可以在不阻塞的情况下高效切换,而 Node.js 在处理大量并发 I/O 时仍然受制于单线程事件循环。
1.4.3 包解析器的 Rust 实现
npm 包解析是 Bun 的核心能力之一。Rust 版本的解析器需要处理 package.json、node_modules 结构、符号链接、exports 字段等复杂逻辑:
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
#[derive(Debug, Deserialize)]
pub struct PackageJson {
pub name: Option<String>,
pub version: Option<String>,
#[serde(rename = "main")]
pub main_entry: Option<String>,
pub exports: Option<serde_json::Value>,
pub dependencies: Option<HashMap<String, String>>,
pub peer_dependencies: Option<HashMap<String, String>>,
}
pub struct PackageResolver {
cache: HashMap<PathBuf, Arc<PackageJson>>,
resolution_cache: HashMap<(PathBuf, String), PathBuf>,
}
impl PackageResolver {
pub fn resolve(&mut self, specifier: &str, from: &Path) -> Result<PathBuf, ResolveError> {
// 检查解析缓存
let key = (from.to_path_buf(), specifier.to_string());
if let Some(cached) = self.resolution_cache.get(&key) {
return Ok(cached.clone());
}
// 处理内置模块
if specifier.starts_with("node:") {
return Ok(PathBuf::from(specifier));
}
// 处理相对/绝对路径
if specifier.starts_with('.') || specifier.starts_with('/') {
return self.resolve_path(specifier, from);
}
// 解析 npm 包
self.resolve_package(specifier, from)
}
fn resolve_package(&mut self, pkg: &str, from: &Path) -> Result<PathBuf, ResolveError> {
let node_modules = find_node_modules(from)?;
let pkg_path = node_modules.join(pkg);
let pkg_json_path = if pkg_path.join("package.json").exists() {
pkg_path.join("package.json")
} else {
// 尝试 @scope/pkgname/style.css 等嵌套路径
pkg_path.join("package.json")
};
let pkg_json: PackageJson = serde_json::from_slice(&fs::read(&pkg_json_path)?)?;
// 处理 exports 字段(支持条件导出)
let resolved = self.resolve_exports(&pkg_json.exports, pkg)?;
self.resolution_cache.insert(key, resolved.clone());
Ok(resolved)
}
}
这段代码展示了 Rust 在处理文件系统遍历、JSON 解析、路径解析等操作时的类型安全优势——所有边界条件都可以通过 Result 类型显式建模,而 Zig 版本中这些边界条件更容易被遗漏。
二、Deno 2.9:deno desktop 的架构哲学
2.1 从"运行时"到"输出目标"的范式转移
Deno Desktop 的核心理念非常清晰:让桌面打包成为 Deno 运行时的一个输出目标,而不是一个新工程。
这是什么意思?我们来对比三种方案:
Electron 方案:
- 前端项目(React/Vue) + main process(Node.js) + preload scripts(桥接层) + IPC 通信 + 打包配置
- 仓库里实际存在两套工程边界:Web 项目 + Electron shell
- 每加一个本地能力:renderer → preload → ipcMain → main → 系统调用,四层串联
Tauri 方案:
- 前端项目 + Rust 后端 + Tauri 插件生态
- 轻量很多(包体 2-10MB vs Electron 的 100MB+),但前端团队需要维护 Rust 代码
- 生态相比 Electron 仍然较新,插件质量参差不齐
Deno Desktop 方案:
- 只有一个项目:Deno/TypeScript 代码
- 桌面打包命令:
deno compile或新的deno desktop - UI 层在 WebView 中运行,逻辑层在 Deno 中执行
- 不存在 preload、ipcMain、main process——binding 直接从 WebView 连接到 Deno runtime
Electron: [renderer] → [preload] → [ipcMain] → [main process] → [文件系统]
↓ ↓ ↓ ↓
4层工程边界,每层需要独立维护
Deno Desktop:[WebView] → [bindings] → [Deno Runtime] → [文件系统]
↓ ↓ ↓
3层结构,bindings 是唯一的桥接层
Deno Desktop 省掉的不是几行代码,而是一整套工程边界的结构性存在。
2.2 deno desktop 的技术原理
deno desktop 基于与 deno compile 相同的底层机制构建,最终输出的是一个包含代码与资源文件的单一可分发二进制文件。关键技术点:
1. WebView 作为 UI 层
- macOS/Windows/Linux 使用系统原生 WebView 渲染 UI(WKWebView / WebView2 / GTK WebKit)
- 不需要打包 Chromium(与 Electron 的根本区别),因此包体显著小于 Electron
2. Deno Runtime 承载业务逻辑
- 所有业务代码仍然在 Deno 运行时中执行
- TypeScript 直接运行,无需额外的编译步骤
- Deno 的权限模型(
--allow-read、--allow-net)在桌面环境中仍然生效
3. 延迟加载优化
Deno 2.9 的桌面性能优化包括:
- 延迟加载
node:全局变量,减少快照体积 - Node 引导程序限制在 Node Worker 中按需执行
- 为延迟加载的 ESM 模块启用 V8 代码缓存
- 快照压缩精简
2.3 实际使用示例
基础桌面应用
// main.ts
import { Application } from "https://deno.land/x/deno_desktop/mod.ts";
const app = new Application({
title: "My Desktop App",
width: 800,
height: 600,
});
app.on("ready", () => {
const window = app.createWindow({
title: "Deno Desktop App",
route: "./index.html",
});
// 直接使用 Deno API,无需任何 IPC 桥接
window.expose("readConfig", async () => {
const content = await Deno.readTextFile("./config.json");
return JSON.parse(content);
});
window.expose("saveConfig", async (data: unknown) => {
await Deno.writeTextFile("./config.json", JSON.stringify(data, null, 2));
return { success: true };
});
});
app.run();
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Deno Desktop</title>
<style>
body { font-family: system-ui; padding: 2rem; }
button { padding: 0.5rem 1rem; cursor: pointer; }
</style>
</head>
<body>
<h1>Deno Desktop App</h1>
<button id="readBtn">读取配置</button>
<button id="saveBtn">保存配置</button>
<pre id="output"></pre>
<script type="module">
// 直接调用 Deno 暴露的函数,无 IPC 样板
const readConfig = await window.readConfig();
document.getElementById("output").textContent = JSON.stringify(readConfig, null, 2);
document.getElementById("saveBtn").onclick = async () => {
const result = await window.saveConfig({ theme: "dark", language: "zh-CN" });
alert(result.success ? "保存成功" : "保存失败");
};
</script>
</body>
</html>
打包命令:
deno compile --allow-read --allow-write --allow-net main.ts
# 生成单个可执行文件,包体 ~40MB
2.4 与 webview_deno 的关系
需要区分两个不同的项目:
deno desktop(官方 Deno 2.9 内置):
- 官方维护,内置在 Deno 运行时中
deno compile的桌面输出目标- API 和命令仍处于 beta 阶段
@webview/webview(社区第三方库):
- 独立维护的第三方绑定包
- 支持多窗口、调试模式等高级功能
- 可以通过
deno add @webview/webview安装
Deno Desktop 是官方在 2.9 中提供的更高级封装,两者共享 WebView 底层但面向不同场景。
2.5 适用边界与局限性
当前适合的场景:
- 内部工具、数据处理脚本的桌面化
- 配置面板、日志查看器等轻量工具
- 技术预研和快速原型验证
当前不适合的场景:
- 需要完整代码签名和自动更新链路的商业应用
- 需要极致包体(<5MB)的嵌入式场景
- 需要完整插件生态的复杂桌面应用
Deno Desktop vs Tauri vs Electron 对比:
| 维度 | Deno Desktop | Tauri | Electron |
|---|---|---|---|
| 包体 | ~40MB | 2-10MB | 100MB+ |
| 语言 | TypeScript | Rust + 前端 | Node.js + 前端 |
| 工程边界 | 单一工程 | Rust + 前端 | main + 前端 |
| 插件生态 | 早期 | 中等 | 成熟 |
| 移动端 | 不支持 | 不支持 | 不支持 |
| 适用阶段 | 技术预研 | 生产级 | 生产级 |
三、两个事件交汇:2026 JS 运行时格局的深层逻辑
3.1 为何两条路径同时出现
Bun 走向 Rust,Deno 走向桌面,这两个选择看似不同,背后却有共同的技术逻辑:
共同逻辑一:系统语言是可靠性的底线
Bun 选择 Rust 是因为 Zig 在大规模工程中可靠性不足;Deno 从一开始就用 Rust 编写(Ryan Dahl 在 Deno 1.0 的架构中就选择了 Rust 作为核心语言)。两条路径最终都收敛到 Rust,说明在 JavaScript 运行时的底层,系统语言的内存安全是不可妥协的。
共同逻辑二:工具链整合是用户体验的核心
Bun 的核心价值是一体化(runtime + bundler + test runner + package manager),Deno Desktop 的核心价值是消除工程边界。两者都在做同一件事:让开发者在更少的工具切换中完成更多的工作。
共同逻辑三:AI 正在成为工程基础设施的一部分
Bun 的 Rust 重写完全由 AI 辅助完成(64 并行 Claude 实例);Claude Code 集成 Bun v1.4.0 作为底层执行引擎;Deno 也推出了 Cloudflare Skills 集成。这意味着AI 不再只是代码生成工具,正在成为工程构建的参与者。
3.2 对 Node.js 的冲击
Node.js 在这场变革中处于一个微妙的位置。
一方面,Node.js 仍然是世界上安装量最大的 JavaScript 运行时,拥有最成熟的生态和最多的生产案例。另一方面,Node.js 的核心架构已经 15 年没有本质变化——单线程事件循环 + libuv I/O 模型在面对 Bun 的多线程设计和 Deno 的安全模型时,显得越来越力不从心。
2026 年,Node.js 也开始引入 worker threads 改进并发模型,引入 corepack 改进包管理,但这些改进都是增量式的,无法与 Bun/Deno 的架构级创新相提并论。
对于普通开发者而言,选型建议是清晰的:
- 现有 Node.js 项目:继续维护,不需要迁移,但可以关注 Bun/Deno 的新特性
- 新项目:根据场景选择——追求极致性能选 Bun,追求安全性和 TypeScript 选 Deno
- 内部工具桌面化:优先尝试 Deno Desktop,降低工程复杂度
3.3 ECMAScript 2026 的背景板
就在 Bun 和 Deno 各自演进的同时,ECMAScript 2026(ES2026)于 2026 年 6 月 30 日正式获批。这是 JavaScript 规范的第 17 个版本,引入了多个值得关注的新特性:
// ES2026 新特性示例
// 1. Math.sum 多参数求和
const total = Math.sum(1, 2, 3, 4, 5); // 15
// 2. Array.groupBy / Map.groupBy 分组
const inventory = [
{ name: "asparagus", type: "vegetables", quantity: 5 },
{ name: "bananas", type: "fruit", quantity: 10 },
];
const grouped = Object.groupBy(inventory, item => item.type);
// { vegetables: [...], fruit: [...] }
// 3. 导入属性(Import Attributes)标准化
import data from "./data.json" with { type: "json" };
// 4. 装饰器(Decorators)标准化
function logged(value, context) {
if (context.kind === "method") {
return function (...args) {
console.log(`Calling ${context.name}`);
return value.call(this, ...args);
};
}
}
class C {
@logged
method() {}
}
这些语言层面的改进,为所有运行时提供了共同的能力基座,也是 JavaScript 生态保持活力的基础。
四、工程实践:如何在实际项目中使用 Bun 和 Deno
4.1 Bun 的生产环境实践
4.1.1 Bun 安装与基础配置
# macOS/Linux
curl -fsSL https://bun.sh/install | bash
# Homebrew
brew install bun
# Windows
powershell -c "irm bun.sh/install.ps1 | iex"
4.1.2 Bun HTTP 服务器实战
// server.ts
import { type ServeOptions } from "bun";
const server = Bun.serve({
port: 3000,
development: process.env.NODE_ENV !== "production",
async fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/api/users" && req.method === "GET") {
const users = await getUsers();
return Response.json(users);
}
if (url.pathname === "/api/users" && req.method === "POST") {
const body = await req.json();
const user = await createUser(body);
return Response.json(user, { status: 201 });
}
// 静态文件服务
return new Response(Bun.file("./public/index.html"));
},
error(error) {
return new Response(`Internal Error: ${error.message}`, { status: 500 });
},
});
console.log(`Server running on http://localhost:${server.port}`);
// 数据库操作示例(Bun 内置 SQLite)
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.run(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
`);
async function getUsers() {
return db.query("SELECT * FROM users").all();
}
async function createUser(data: { name: string; email: string }) {
const stmt = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
const result = stmt.run(data.name, data.email);
return { id: result.lastInsertRowId, ...data };
}
4.1.3 Bun 的性能对比测试
# 使用 hey 进行压测(macOS: brew install hey)
hey -n 100000 -c 100 -m GET http://localhost:3000/api/users
Bun 在简单 JSON API 场景下通常比 Node.js 快 3-5 倍,内存占用低 30%-50%。
4.2 Deno 2.9 桌面应用开发
4.2.1 安装 Deno 并启用 Canary 版本
# 标准安装
curl -fsSL https://deno.land/install.sh | sh
# 升级到 Canary(获取 deno desktop)
deno upgrade canary
# 验证版本
deno --version
# Deno 2.9.0-canary.20260702 或更高
4.2.2 开发日志查看器
// log_viewer.ts
import { Application } from "jsr:@deno/app@0.1.0";
const app = new Application({
title: "日志查看器",
width: 1200,
height: 800,
});
app.on("ready", async () => {
const win = await app.createWindow({
title: "日志查看器 v1.0",
route: "./ui/index.html",
});
// 暴露日志读取 API
win.expose("readLogFile", async (filePath: string) => {
try {
const stat = await Deno.stat(filePath);
if (!stat.isFile) {
return { error: "不是有效的文件" };
}
const content = await Deno.readTextFile(filePath);
const lines = content.split("\n").map((line, i) => ({
line: i + 1,
content: line,
level: detectLogLevel(line),
}));
return { success: true, lines, total: lines.length };
} catch (err) {
return { error: `读取失败: ${err.message}` };
}
});
// 暴露文件选择对话框
win.expose("selectLogFile", async () => {
const options = {
title: "选择日志文件",
filters: [
{ name: "日志文件", extensions: ["log", "txt"] },
{ name: "所有文件", extensions: ["*"] },
],
};
// 使用原生对话框(需要相应权限)
return { path: null }; // 简化示例,实际需要桥接原生对话框
});
// 暴露实时 tail 功能
win.expose("tailFile", async (filePath: string, onLine: (line: string) => void) => {
const file = await Deno.open(filePath, { read: true });
const decoder = new TextDecoder();
let position = 0;
for await (const chunk of file.readable) {
const text = decoder.decode(chunk);
const newLines = text.split("\n").filter(l => l.length > 0);
for (const line of newLines) {
onLine(line);
}
position += chunk.length;
}
});
});
app.run();
<!-- ui/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>日志查看器</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "SF Mono", "Fira Code", "Consolas", monospace;
background: #1e1e1e;
color: #d4d4d4;
height: 100vh;
display: flex;
flex-direction: column;
}
.toolbar {
background: #252526;
padding: 0.75rem 1rem;
border-bottom: 1px solid #333;
display: flex;
gap: 0.5rem;
align-items: center;
}
.toolbar button {
background: #0e639c;
color: white;
border: none;
padding: 0.4rem 1rem;
border-radius: 2px;
cursor: pointer;
font-size: 0.85rem;
}
.toolbar button:hover { background: #1177bb; }
.toolbar .path-display {
color: #888;
font-size: 0.8rem;
margin-left: auto;
}
.log-container {
flex: 1;
overflow-y: auto;
padding: 0.5rem;
}
.log-line {
display: flex;
padding: 2px 0;
border-bottom: 1px solid #2d2d2d;
}
.log-line:hover { background: #2a2d2e; }
.line-number {
color: #858585;
min-width: 50px;
text-align: right;
padding-right: 1rem;
user-select: none;
}
.line-content { flex: 1; white-space: pre-wrap; word-break: break-all; }
.level-error { color: #f14c4c; }
.level-warn { color: #cca700; }
.level-info { color: #3794ff; }
.level-debug { color: #89d185; }
.status-bar {
background: #007acc;
color: white;
padding: 0.25rem 1rem;
font-size: 0.8rem;
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<div class="toolbar">
<button id="openBtn">打开文件</button>
<button id="tailBtn">实时跟踪</button>
<button id="clearBtn">清空</button>
<input type="text" id="filterInput" placeholder="过滤..."
style="padding: 0.3rem; font-size: 0.85rem; width: 200px;">
<span class="path-display" id="currentPath">未选择文件</span>
</div>
<div class="log-container" id="logContainer"></div>
<div class="status-bar">
<span id="statusText">就绪</span>
<span id="lineCount">0 行</span>
</div>
<script type="module">
const container = document.getElementById("logContainer");
const pathDisplay = document.getElementById("currentPath");
const statusText = document.getElementById("statusText");
const lineCountEl = document.getElementById("lineCount");
const filterInput = document.getElementById("filterInput");
let allLines = [];
let filter = "";
filterInput.addEventListener("input", (e) => {
filter = e.target.value.toLowerCase();
renderLines();
});
document.getElementById("openBtn").onclick = async () => {
// 实际使用需要通过 Deno 桥接原生文件对话框
const result = await window.readLogFile("/var/log/system.log");
if (result.error) {
alert(result.error);
return;
}
allLines = result.lines;
pathDisplay.textContent = "/var/log/system.log";
statusText.textContent = `已加载 ${result.total} 行`;
lineCountEl.textContent = `${result.total} 行`;
renderLines();
};
function detectLogLevel(line) {
if (line.includes("ERROR") || line.includes("[E]")) return "error";
if (line.includes("WARN") || line.includes("[W]")) return "warn";
if (line.includes("INFO") || line.includes("[I]")) return "info";
if (line.includes("DEBUG") || line.includes("[D]")) return "debug";
return "info";
}
function renderLines() {
container.innerHTML = "";
const filtered = filter
? allLines.filter(l => l.content.toLowerCase().includes(filter))
: allLines;
for (const { line: lineNum, content, level } of filtered.slice(-500)) {
const div = document.createElement("div");
div.className = "log-line";
div.innerHTML = `
<span class="line-number">${lineNum}</span>
<span class="line-content level-${level}">${escapeHtml(content)}</span>
`;
container.appendChild(div);
}
container.scrollTop = container.scrollHeight;
lineCountEl.textContent = `${filtered.length} / ${allLines.length} 行`;
}
function escapeHtml(str) {
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
}
document.getElementById("clearBtn").onclick = () => {
allLines = [];
container.innerHTML = "";
lineCountEl.textContent = "0 行";
statusText.textContent = "已清空";
};
</script>
</body>
</html>
4.3 性能基准测试:三者横向对比
为了提供实际数据,我们设计了一组基准测试:
// benchmark.ts - 使用 Bun 的内置基准测试
import { run } from "bun";
const results = await run({
samples: 10,
threads: true,
beforeEach: () => ({ data: new Array(10000).fill(0).map((_, i) => ({ id: i, value: Math.random() })) }),
}, ({ data }) => {
// 测试 1: 过滤 + 映射 + 排序
const filtered = data
.filter(x => x.value > 0.5)
.map(x => ({ ...x, doubled: x.value * 2 }))
.sort((a, b) => a.value - b.value);
return filtered;
});
console.table(results);
理论性能对比(基于公开基准测试数据):
| 场景 | Node.js | Bun | Deno |
|---|---|---|---|
| HTTP Hello World (req/s) | ~30,000 | ~120,000 | ~25,000 |
| JSON 序列化 | 基准 1x | 2-3x | ~1.2x |
| npm install (100 deps) | ~15s | ~2s | ~8s (缓存) |
| TypeScript 类型检查 | 需 tsc (~5s) | 内置 (~0.5s) | 内置 (~1s) |
| 冷启动时间 | ~200ms | ~20ms | ~150ms |
| SQLite 查询 | 需第三方库 | 内置 | 需第三方库 |
注:以上数据基于公开基准测试,实际性能受具体场景影响较大。
五、开发者选型决策树
面对 Bun、Deno、Node.js 三个运行时,开发者应该如何选择?以下是实操性的决策参考:
你的首要需求是什么?
│
├─ 追求极致运行时性能
│ └─ Bun(多线程 JS 引擎,启动速度快)
│
├─ 追求 TypeScript 最佳体验
│ ├─ Deno(原生 TS,无任何配置)
│ └─ Bun(内置 TS 支持,启动快)
│
├─ 桌面应用开发
│ ├─ Electron(Tauri)→ 独立桌面工程
│ └─ Deno Desktop → 与 Web 项目共享工程
│
├─ AI 编程工具集成
│ ├─ Claude Code → 推荐 Bun v1.4.0
│ └─ Cursor/Copilot → 任意运行时均可
│
└─ 已有 Node.js 项目维护
└─ 继续用 Node.js,无需迁移
一个重要的提醒:不要因为新鲜感而迁移生产项目。Bun 和 Deno Desktop 在 2026 年仍然处于快速迭代期,API 变化可能带来维护成本。现有的 Node.js + Electron 组合虽然"不够性感",但胜在稳定和社区支持。
六、2026 年 JavaScript 运行时的未来展望
6.1 WebAssembly 作为第三极
除了 Bun 和 Deno,WebAssembly(Wasm)正在成为 JavaScript 运行时的"第三极"。
WasmComponentModel(WASIP2)的成熟,使得用 Rust/C/C++ 编译的 Wasm 模块可以在浏览器和服务器环境中无缝运行。Deno 和 Bun 都支持 Wasm,Node.js 通过 wasmtime 也能运行 Wasm。
这意味着未来的 JavaScript 运行时竞争不仅仅是"谁更快",还包括谁能更好地整合多语言生态。
6.2 AI 与运行时的深度绑定
Anthropic 收购 Bun、Claude Code 集成 Bun、Cloudflare Skills 支持 Deno——这些信号指向一个更大的趋势:AI 编程工具与底层运行时的绑定会越来越紧密。
未来可能出现这样的情况:
- AI 生成的代码片段直接在 Bun 运行时中执行,无需调用外部服务
- AI 编程工具通过 Deno 的安全沙箱运行不受信任的用户代码
- 运行时的性能特性(多线程、冷启动速度)直接影响 AI 代码生成的可用性
6.3 工具链的统一趋势
Bun 的"一体化"策略正在被整个生态认可。npm + TypeScript + Jest + Webpack 的组合正在被 Bun/Deno 的内置工具链替代。这个趋势的底层逻辑是:开发体验的一致性比生态丰富性更重要。
对于 2026 年后的新项目,开发者应该优先考虑那些开箱即用、配置极少的工具链,而不是那些功能强大但配置复杂的组合。
结语:好戏才刚刚开始
Bun 放弃 Zig 全面拥抱 Rust,Deno 2.9 杀入桌面,这两个事件的交汇点不在于"谁取代了谁",而在于揭示了一个更大的趋势:JavaScript 运行时的工程范式正在经历一次根本性的重构。
从"功能集成"到"架构收敛",从"JavaScript only"到"多语言融合",从"人工开发"到"AI 辅助构建"——2026 年的 JavaScript 运行时大战,本质上是一场关于"未来开发范式"的竞争。
作为开发者,我们不需要急着站队,但需要持续关注。因为这场竞争的结果,将直接影响我们未来五年的开发体验和职业路径。
好戏,才刚刚开始。
参考来源:
- IT之家:《11 天狂写 100 万行代码:Rust 重构 JavaScript 工具 Bun》(2026-07-11)
- 腾讯云:《Deno Desktop 替你省掉了 Electron 的 preload 和 IPC 样板》(2026-06-24)
- 腾讯网:《Deno 2.9 发布:简化桌面应用开发,性能全面提升》(2026-07-02)
- 企鹅号:《Claude Code 已整合 Rust 重构版 Bun》(2026-07-24)
- ECMA International:ECMAScript 2026 正式获批(2026-06-30)
- PostgreSQL 18 已发布(2026-07-30)