编程 程序员茄子的 2026 深度复盘:Bun 放弃 Zig 全面 Rust 化、Deno 2.9 杀入桌面——JS 运行时大战进入新纪元

2026-07-30 23:44:23 +0800 CST views 8

程序员茄子的 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 clippymiri 进行内存安全检查,而 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 DesktopTauriElectron
包体~40MB2-10MB100MB+
语言TypeScriptRust + 前端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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    }
    
    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.jsBunDeno
HTTP Hello World (req/s)~30,000~120,000~25,000
JSON 序列化基准 1x2-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)
复制全文 生成海报 Bun Rust Zig Deno Node.js WebAssembly AI JavaScript

推荐文章

虚拟DOM渲染器的内部机制
2024-11-19 06:49:23 +0800 CST
程序员茄子在线接单