编程 Bun 1.3.14 深度实战:当 JavaScript 运行时学会「图像处理」——从内置图片 API 到 HTTP/3 的全能进化之路(2026)

2026-06-13 11:16:35 +0800 CST views 4

Bun 1.3.14 深度实战:当 JavaScript 运行时学会「图像处理」——从内置图片 API 到 HTTP/3 的全能进化之路(2026)

引言:Bun 不再只是一个「快的 Node.js」

如果你对 Bun 的印象还停留在「比 Node.js 启动快 4 倍的 JS 运行时」,那 1.3.14 版本会彻底颠覆你的认知。

2026 年 5 月 13 日,Bun 发布了 v1.3.14,一次性修复了 92 个问题(覆盖社区 380+ 👍),并带来了几个重量级新特性:

  • Bun.Image — 内置图片处理 API,零依赖替代 sharp
  • Global Virtual Store — 7 倍加速暖安装
  • HTTP/3 (QUIC) — Bun.serve() 原生支持 QUIC 协议
  • HTTP/2 客户端 — fetch() 实验性支持 HTTP/2 多路复用
  • fs.watch() 重写 — 直连操作系统原生文件监听
  • --no-orphans — 父进程退出时自动终止子进程树
  • Bun.Terminal on Windows — 通过 ConPTY 实现跨平台终端控制
  • process.execve() — POSIX 进程替换
  • FreeBSD & Android 构建

更值得关注的背景是:Bun 已于 2025 年 12 月加入 Anthropic,成为 Claude Code 和 Claude Agent SDK 的基础设施。这意味着 Bun 的发展速度只会越来越快。

本文将从架构设计、代码实战、性能对比、生产级部署四个维度,深入剖析 Bun 1.3.14 的每一个核心特性,帮你判断:Bun 是否已经准备好成为你的生产级运行时?


一、Bun.Image:内置图片处理,告别 sharp 和 native 模块地狱

1.1 为什么这是个大新闻?

在后端开发中,图片处理几乎是一个绕不开的需求:缩略图、格式转换、裁剪旋转、水印叠加。传统方案是安装 sharp——但它依赖 libvips C 库,在不同操作系统和 CI 环境中经常出问题,Docker 镜像也要因此增大几百 MB。

Bun 1.3.14 直接在运行时内置了图片处理能力,API 设计精简,链式调用,性能比 sharp 更快

1.2 支持的格式

格式macOSWindowsLinux
JPEG
PNG
WebP
GIF
BMP
TIFF✅ 解码✅ 解码
HEIC✅ 解码+编码✅ 解码+编码
AVIF✅ 解码(Apple Silicon 可编码)✅ 解码+编码

核心格式(JPEG、PNG、WebP、GIF、BMP)使用静态链接编解码器,跨平台输出一致。HEIC、AVIF、TIFF 使用系统后端(macOS 用 ImageIO + vImage,Windows 用 WIC),懒加载符号解析实现零启动成本。

1.3 API 设计与代码实战

Bun.Image 采用链式管道设计,所有变换操作异步执行(metadata() 除外),不阻塞主线程:

import { Bun } from "bun";

// 基础用法:读取、缩放、转换格式、写入
await Bun.file("photo.jpg")
  .image()
  .resize(1024, 1024, { fit: "inside" })
  .rotate(90)
  .webp({ quality: 85 })
  .write("thumb.webp");

多种输入源支持:

// 从 ArrayBuffer 零拷贝
const buffer = await fetch(url).then(r => r.arrayBuffer());
const img = new Bun.Image(buffer);

// 从 Blob
const blob = new Blob([arrayBuffer], { type: "image/jpeg" });
const img2 = blob.image();

// 从 data: URL
const img3 = new Bun.Image("data:image/png;base64,iVBOR...");

// 从 Bun.file()
const img4 = Bun.file("input.png").image();

// 从 S3File(Bun 内置 S3 支持)
const s3File = Bun.S3.file("my-bucket", "photos/avatar.jpg");
const img5 = await s3File.image();

链式变换:

import { Bun } from "bun";

const result = await new Bun.Image("input.jpg")
  // 缩放:指定宽高
  .resize(800, 600, { filter: "lanczos3", fit: "cover" })
  // 旋转(仅支持 90/180/270)
  .rotate(90)
  // 水平翻转
  .flip()
  // 垂直翻转
  .flop()
  // 调整亮度、饱和度
  .modulate({ brightness: 1.1, saturation: 0.8 })
  // 输出格式
  .jpeg({ quality: 90 });

Resize 模式详解:

// fit 选项决定了缩放行为:
// "cover"  — 填满目标区域,裁剪多余部分
// "inside" — 完整显示图片,不超出目标区域(保持宽高比)
// "outside" — 填满至少一个维度,可能超出另一个
// "contain" — 类似 inside,居中放置

// withoutEnlargement:小图不放大
await Bun.file("icon.png")
  .image()
  .resize(256, 256, {
    fit: "inside",
    withoutEnlargement: true  // 如果原图更小,保持原尺寸
  })
  .png()
  .write("icon-scaled.png");

// filter 选项(卷积核算法):
// "nearest"   — 最近邻插值(最快,质量最差)
// "box"       — 盒式滤波
// "bilinear"  — 双线性插值
// "cubic"     — 三次卷积
// "mitchell"  — Mitchell-Netravali 滤波
// "lanczos2"  — Lanczos 窗函数(2 lobes)
// "lanczos3"  — Lanczos 窗函数(3 lobes,最慢但质量最好)
// "mks2013"   — Modified Kernel Spline 2013
// "mks2021"   — Modified Kernel Spline 2021

多种输出方式:

const img = Bun.file("photo.jpg").image();

// 获取字节数据
const bytes: Uint8Array = await img.resize(200).jpeg().bytes();

// 获取 Buffer
const buf: Buffer = await img.resize(200).jpeg().buffer();

// 获取 Blob
const blob: Blob = await img.resize(200).jpeg().blob();

// 获取 Base64
const base64: string = await img.resize(200).jpeg().toBase64();

// 获取 Data URL
const dataUrl: string = await img.resize(200).jpeg().dataurl();

// 获取元数据
const meta = await new Bun.Image("photo.jpg").metadata();
console.log(meta);
// { width: 1920, height: 1080, format: "jpeg", ... }

// 生成 ThumbHash 占位图(用于 blur-up 效果)
const placeholder: string = await Bun.file("hero.jpg")
  .image()
  .resize(200)
  .placeholder();  // 返回 thumbhash data URL

1.4 与 HTTP Response 集成

这是最优雅的设计——Bun.Image 可以直接作为 Response 的 body:

// 直接在 HTTP handler 中使用
Bun.serve({
  port: 3000,
  async fetch(req) {
    const path = new URL(req.url).pathname;

    if (path.startsWith("/thumb/")) {
      const filename = path.slice(7);
      return new Response(
        new Bun.Image(`uploads/${filename}`).resize(200, 200, { fit: "cover" }).jpeg()
        // 自动设置 Content-Type: image/jpeg
      );
    }

    if (path.startsWith("/avatar/")) {
      const userId = path.slice(8);
      const avatar = Bun.S3.file("avatars", `${userId}.webp`);
      const s3Img = await avatar.image();
      return new Response(
        s3Img.resize(128).webp({ quality: 80 })
        // 自动设置 Content-Type: image/webp
      );
    }

    return new Response("Not found", { status: 404 });
  }
});

1.5 生产级图片处理服务

下面是一个完整的图片处理微服务,支持多种操作参数:

// image-service.ts
import { Bun } from "bun";

interface ProcessParams {
  width?: number;
  height?: number;
  format?: "jpeg" | "png" | "webp";
  quality?: number;
  fit?: "cover" | "inside" | "outside";
  rotate?: 90 | 180 | 270;
  flip?: boolean;
  flop?: boolean;
}

function parseParams(url: string): ProcessParams & { filename: string } {
  const u = new URL(url);
  const params: ProcessParams & { filename: string } = {
    filename: u.pathname.replace("/process/", ""),
  };

  if (u.searchParams.has("w")) params.width = parseInt(u.searchParams.get("w")!);
  if (u.searchParams.has("h")) params.height = parseInt(u.searchParams.get("h")!);
  if (u.searchParams.has("f")) params.format = u.searchParams.get("f") as any;
  if (u.searchParams.has("q")) params.quality = parseInt(u.searchParams.get("q")!);
  if (u.searchParams.has("fit")) params.fit = u.searchParams.get("fit") as any;
  if (u.searchParams.has("rotate")) params.rotate = parseInt(u.searchParams.get("rotate")!) as any;
  if (u.searchParams.has("flip")) params.flip = true;
  if (u.searchParams.has("flop")) params.flop = true;

  return params;
}

const server = Bun.serve({
  port: 3001,
  async fetch(req) {
    if (req.method !== "GET") {
      return new Response("Method not allowed", { status: 405 });
    }

    const params = parseParams(req.url);

    // 安全校验:防止路径遍历
    if (params.filename.includes("..") || params.filename.startsWith("/")) {
      return new Response("Invalid filename", { status: 400 });
    }

    // 限制最大尺寸防止滥用
    if ((params.width && params.width > 4096) ||
        (params.height && params.height > 4096)) {
      return new Response("Dimensions too large", { status: 400 });
    }

    try {
      const imgPath = `./images/${params.filename}`;
      let pipeline = Bun.file(imgPath).image();

      if (params.width || params.height) {
        pipeline = pipeline.resize(params.width, params.height, {
          fit: params.fit || "inside",
          withoutEnlargement: true,
        });
      }

      if (params.rotate) pipeline = pipeline.rotate(params.rotate);
      if (params.flip) pipeline = pipeline.flip();
      if (params.flop) pipeline = pipeline.flop();

      // 默认输出 WebP
      const format = params.format || "webp";
      const quality = params.quality || 85;

      let output;
      switch (format) {
        case "jpeg":
          output = pipeline.jpeg({ quality });
          break;
        case "png":
          output = pipeline.png();
          break;
        case "webp":
        default:
          output = pipeline.webp({ quality });
          break;
      }

      return new Response(output);
    } catch (err) {
      if (err instanceof Error && err.message.includes("No such file")) {
        return new Response("Image not found", { status: 404 });
      }
      return new Response("Processing error", { status: 500 });
    }
  },
});

console.log(`Image processing service running at http://localhost:${server.port}`);

// 使用示例:
// GET /process/photo.jpg?w=800&h=600&fit=cover&f=webp&q=85
// GET /process/avatar.png?w=128&rotate=90
// GET /process/banner.jpg?w=1200&h=630&format=jpeg

1.6 性能对比:Bun.Image vs sharp

官方在 linux/x64 上以 50 次迭代基准测试了 Bun.Image 和 sharp 0.34.5(sharp.concurrency(1)):

操作Bun.Imagesharp加速比
metadata()0.004 ms0.28 ms70×
1080p PNG → 400×400 JPEG28.6 ms39.5 ms1.38×
1080p PNG → 800×600 WebP82.7 ms110.1 ms1.33×
4K JPEG → 800×450 JPEG35.8 ms45.5 ms1.27×
4K JPEG → 1920×1080 JPEG57.2 ms69.9 ms1.22×
12MP JPEG → 1024×768 WebP138 ms165 ms1.20×

metadata() 查询快了 70 倍——这是因为 Bun.Image 直接从文件头读取元数据,无需解码整个图片。缩放操作也有 20-38% 的性能提升。

性能优势来自:i16 定点 SIMD 缩放内核、JPEG IDCT 缩放到最小充足尺寸、零拷贝 ArrayBuffer 借用、以及预分配的单 arena 用于缩放临时内存。


二、Global Virtual Store:7 倍加速 CI 依赖安装

2.1 问题背景

在 CI/CD 环境中,每次构建都要安装依赖。即使有 lockfile 和缓存,bun install --linker=isolated(类似 pnpm 的隔离 node_modules)在 macOS 上依然很慢——原因是 APFS 的 clonefileat() 在操作时持有卷级内核锁,导致并行化几乎无效。

2.2 解决方案

Bun 1.3.14 引入了 Global Virtual Store(全局虚拟存储),将隔离链接器的行为从「每个项目 clone 一份」变为「全局存储一份,项目只建 symlink」。

# bunfig.toml
[install]
globalStore = true

或通过环境变量:

BUN_INSTALL_GLOBAL_STORE=1 bun install

原理: 包从不可变缓存源(npm registry、git、tarball)解析后,存入全局 <cache>/links/ 目录。每个项目的 node_modules/.bun/<pkg>@<ver> 变为指向全局存储的符号链接。

安全规则: 只有来自不可变缓存源且未被修改、没有受信任生命周期脚本的包才符资格进入全局存储。不符合条件的包自动回退到项目级拷贝。

2.3 性能数据

官方在 Apple Silicon macOS 上测试了约 1400 个包的 fixture:

场景耗时系统时间clonefileat 调用数
--linker hoisted823 ms478 ms1,387
--linker isolated (之前)841 ms1,256 ms1,387
--linker isolated + globalStore115 ms94 ms0

暖安装从 841ms 降到 115ms,7.3 倍加速。 clonefileat 调用从 1387 次降为 0 次——因为不再需要文件级拷贝,只做 symlink。

2.4 实战配置

// 项目结构:
// apps/
//   web/
//     package.json
//     bun.lockb
//   api/
//     package.json
//     bun.lockb
// packages/
//   shared/
//     package.json
//     bun.lockb
// bunfig.toml

// 根目录 bunfig.toml — 全局启用 Global Virtual Store
// [install]
// globalStore = true

// CLI 配置 workspace
// bun install
// 
// 效果:
// - 多个 workspace 共享相同版本的依赖(只存一份)
// - 暖安装(CI 场景)几乎瞬间完成
// - 不同版本解析的包会自动获得独立存储条目

CI Dockerfile 示例:

FROM oven/bun:1.3.14 AS base
WORKDIR /app

# 配置全局存储
ENV BUN_INSTALL_GLOBAL_STORE=1

# 利用 Docker 层缓存
COPY bun.lockb package.json ./
RUN bun install --frozen-lockfile

COPY . .
RUN bun build ./src/index.ts --outdir ./dist --target bun

FROM base AS runner
COPY --from=base /app/dist ./dist
COPY --from=base /app/node_modules ./node_modules
EXPOSE 3000
CMD ["bun", "run", "./dist/index.js"]

三、HTTP/3 (QUIC):Bun.serve() 原生支持下一代传输协议

3.1 为什么 HTTP/3 重要?

HTTP/3 基于 QUIC 协议(UDP),解决了 TCP + TLS 1.2 的几个根本性瓶颈:

  1. 连接建立慢: TCP 需要三次握手,TLS 又需要额外往返。QUIC 将传输和加密握手合并为一次往返。
  2. 队头阻塞 (Head-of-Line Blocking): TCP 上一个丢包会阻塞所有后续流。QUIC 的流独立,互不影响。
  3. 连接迁移: QUIC 使用 Connection ID 而非四元组标识连接,Wi-Fi 切 4G 时连接不中断。

3.2 Bun 的 HTTP/3 实现

Bun 使用 lsquic v4.6.2 作为 QUIC 协议栈。启用方式极其简单——一个配置项:

import { Bun } from "bun";

const tls = {
  cert: await Bun.file("/etc/ssl/cert.pem").text(),
  key: await Bun.file("/etc/ssl/key.pem").text(),
};

Bun.serve({
  port: 443,
  tls,
  http3: true,  // 一行代码启用 HTTP/3
  fetch(req) {
    return new Response("Hello over HTTP/3!", {
      headers: { "content-type": "text/plain" },
    });
  },
});

启用 http3: true 后,Bun 同时绑定:

  • TCP 端口 → HTTP/1.1 + HTTP/2
  • UDP 端口 → HTTP/3

所有协议共享同一个 fetch handler,无需任何代码修改。HTTP/1.1 和 HTTP/2 响应自动包含 Alt-Svc: h3=":<port>"; ma=86400,浏览器会自动发现 QUIC 端点。

3.3 性能数据

Linux x64 单进程 loopback 基准测试:

场景HTTP/3HTTPS/1.1HTTP/1.1
静态路由 (routes)509,135 req/s189,130 req/s239,476 req/s
动态 fetch handler283,485 req/s142,323 req/s171,696 req/s

HTTP/3 在静态路由场景下比 HTTPS/1.1 快了 2.7 倍,比纯 HTTP/1.1 快了 2.1 倍。动态场景下也有约 2 倍的提升。

注意:约 50% 的 HTTP/3 CPU 时间消耗在 lsquic 内部,未来优化空间仍然很大。

3.4 高级配置

import { Bun } from "bun";

// 仅启用 HTTP/3(禁用 HTTP/1.1)
Bun.serve({
  port: 443,
  tls: {
    cert: "...",
    key: "...",
  },
  http3: true,
  http1: false,  // 只接受 HTTP/3 连接
  fetch(req) {
    // 支持 streaming response
    const stream = new ReadableStream({
      async start(controller) {
        for (let i = 0; i < 10; i++) {
          controller.enqueue(`chunk ${i}\n`);
          await Bun.sleep(100);
        }
        controller.close();
      },
    });
    return new Response(stream);
  },
});

// 文件服务
Bun.serve({
  port: 443,
  tls,
  http3: true,
  fetch(req) {
    const path = new URL(req.url).pathname;
    return new Response(Bun.file(`./public${path}`));
  },
});

3.5 已知限制

  • WebSocket over HTTP/3 尚不支持(server.upgrade() 返回 false)
  • 0-RTT 未启用
  • Unix socket 跳过 H3 监听器
  • 不支持 trailer 和 Expect: 100-continue
  • ⚠️ 不要在生产环境部署 http3: true——目前仍是实验性功能

四、HTTP/2 客户端:fetch() 的多路复用革命

4.1 从连接数到连接复用

传统 HTTP/1.1 下,每个 fetch 都需要建立独立的 TCP+TLS 连接(虽然有 keep-alive,但并发请求仍受限于连接数)。HTTP/2 的多路复用让多个请求共享一个连接,通过 stream ID 区分。

Bun 1.3.14 为 fetch() 添加了实验性 HTTP/2 支持:

// 全局启用 HTTP/2
// BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT=1 bun run app.js
// 或
// bun run --experimental-http2-fetch app.js

// 单请求指定协议
const res = await fetch("https://api.example.com/data", {
  protocol: "http2",
});

4.2 连接合并 (Connection Coalescing)

多个并行 fetch 到同一 origin 时,共享一个 TLS 握手和一个 TCP 连接

// 这三个请求共享同一个 HTTP/2 连接
const [users, posts, comments] = await Promise.all([
  fetch("https://api.example.com/users", { protocol: "http2" }),
  fetch("https://api.example.com/posts", { protocol: "http2" }),
  fetch("https://api.example.com/comments", { protocol: "http2" }),
]);

第一个请求打开 socket,后续请求附加到同一个 HTTP/2 session,直到达到服务器的 MAX_CONCURRENT_STREAMS 限制(超出部分自动排队)。

4.3 协议控制

// 强制 HTTP/2(服务器不支持则报错 HTTP2Unsupported)
await fetch("https://example.com", { protocol: "http2" });
await fetch("https://example.com", { protocol: "h2" });  // 等效

// 强制 HTTP/1.1(忽略实验性标志)
await fetch("https://example.com", { protocol: "http1.1" });
await fetch("https://example.com", { protocol: "h1" });  // 等效

4.4 HTTP/3 客户端

fetch() 也支持实验性 HTTP/3 客户端:

// BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP3_CLIENT=1 bun app.ts
const res = await fetch("https://example.com/", { protocol: "http3" });

Alt-Svc 自动升级: 启用 --experimental-http3-fetch 后,当服务器响应包含 Alt-Svc: h3=":443" 头时,后续请求自动切换到 QUIC。

4.5 HTTP/2 安全加固

Bun 的 HTTP/2 客户端实现了多项 RFC 9113 安全防护:

  • CONTINUATION flood / HPACK bomb 缓解: header block 累积和解码列表各 256 KiB 上限
  • PING 反射攻击缓解: 排队的 PING/SETTINGS-ACK 控制帧 1 MiB 上限
  • 首帧必须为 SETTINGS(否则断开)
  • REFUSED_STREAM 仅在无数据交付时重试(最多 5 次)
  • Content-Length 与 DATA 帧字节数不匹配则拒绝
  • GOAWAY 不再丢弃已完成流

五、fs.watch() 重写:直连系统原生文件监听

5.1 之前的问题

Bun 的旧 fs.watch() 实现路由通过内部 bundler watcher,带来了多个 bug:

  • Linux recursive: true 无法追踪 watch 之后新建的目录
  • 删除再重建的文件,后续变更不再触发事件
  • macOS 同时启动 kqueue + FSEvents 两个 watcher 线程

5.2 新实现

新实现直连操作系统原生 API:

  • Linux: inotify
  • macOS: FSEvents(独占,不再双线程)
  • FreeBSD: kqueue
import fs from "node:fs";

// Linux: 新建的目录现在也能被追踪
fs.watch("./src", { recursive: true }, (event, filename) => {
  console.log(event, filename);
});

// mkdir src/newDir && touch src/newDir/file.txt
// 之前: 只输出 "rename newDir"(file.txt 被遗漏)
// 现在: "rename newDir", "rename newDir/file.txt", "change newDir/file.txt"

5.3 生产应用:热重载开发服务器

// dev-server.ts
import { Bun } from "bun";
import fs from "node:fs";
import path from "node:path";

const WATCHED_DIR = "./src";
const DEBOUNCE_MS = 100;

let debounceTimer: Timer | null = null;

function onFileChange(event: string, filename: string | null) {
  if (!filename) return;
  
  // 忽略 .swp, .DS_Store 等临时文件
  if (filename.endsWith(".swp") || filename.endsWith(".DS_Store")) return;

  // 防抖:合并短时间内多次变更
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(() => {
    console.log(`[${new Date().toISOString()}] ${event}: ${filename}`);
    
    // 通知客户端热更新
    // ... WebSocket broadcast logic
  }, DEBOUNCE_MS);
}

// 启动文件监听
fs.watch(WATCHED_DIR, { recursive: true }, onFileChange);

// 启动开发服务器
Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Dev server running");
  },
});

console.log("Dev server started. Watching for changes...");

六、--no-orphans:进程生命周期管理

6.1 问题描述

当 Bun 被父进程(Electron、CI runner、shell shim)启动时,如果父进程被 SIGKILL(无法转发信号),Bun 和它 spawn 的子进程会被重新挂载到 init/launchd 下,变成孤儿进程持续运行——浪费资源甚至造成数据损坏。

6.2 解决方案

# CLI 标志
bun --no-orphans run my-script.ts

# bunfig.toml
[run]
noOrphans = true

# 环境变量
BUN_FEATURE_FLAG_NO_ORPHANS=1 bun run my-script.ts

启用后,Bun 会自动检测父进程退出(即使父进程是被 SIGKILL 的),然后:

  1. 递归 SIGKILL 所有子孙进程
  2. 使用 SIGSTOP → 验证 PPID → SIGKILL 策略防止误杀 pid 回收后的无关进程

实现原理:

  • Linux:prctl(PR_SET_PDEATHSIG, SIGKILL) — 内核级,无轮询,无额外线程
  • macOS:在事件循环的 kqueue 上注册 EVFILT_PROC/NOTE_EXIT 监视原始父 PID

该标志会被嵌套 Bun 进程自动继承,顶层启用一次即可。

6.3 实战:Electron + Bun 混合架构

// main-process.ts (Electron 主进程)
import { app, BrowserWindow } from "electron";
import { spawn } from "child_process";

let bunProcess: any;

app.whenReady().then(() => {
  const win = new BrowserWindow({ width: 1200, height: 800 });
  
  // 启动 Bun 后端服务
  bunProcess = spawn("bun", ["--no-orphans", "run", "./backend/server.ts"], {
    stdio: "pipe",
  });

  // 如果 Electron 被 SIGKILL,Bun 后端会自动退出
  // 不会残留端口占用或数据库连接
});

七、Bun.Terminal on Windows:跨平台终端控制

7.1 背景

之前 Bun.Terminal 和 Bun.spawn({ terminal }) 只在 macOS 和 Linux 上可用。1.3.14 通过 Windows ConPTY (CreatePseudoConsole) API 实现了跨平台支持。

import { Bun } from "bun";

const terminal = new Bun.Terminal({
  cols: 80,
  rows: 24,
  onData(data: Uint8Array) {
    process.stdout.write(data);
  },
});

const proc = Bun.spawn({
  cmd: ["cmd.exe", "/c", "echo", "hello from ConPTY"],
  terminal,
});

await proc.exited;
terminal.close();

7.2 平台差异

特性POSIXWindows
termios (inputFlags, outputFlags, localFlags, controlFlags)❌(始终为 0)
无进程时的 echo✅ 内核行纪律❌ ConPTY 无行纪律,输入缓冲到下一读者
输出编码字节精确ConPTY 重编码(语义等价但非字节一致)

八、其他重要更新

8.1 process.execve()

// POSIX 系统调用:替换当前进程映像
// 成功后不返回
process.execve("/usr/bin/node", ["node", "app.js"], {
  PATH: process.env.PATH,
});

// ^ 成功则这行不会执行

// 行为:
// - stdio 被继承(fd 0/1/2)
// - 其他 fd 标记 close-on-exec
// - 信号 mask 在调用 execve 前重置
// - Worker 线程中调用抛出 ERR_WORKER_UNSUPPORTED_OPERATION
// - Windows 上抛出 ERR_FEATURE_UNAVAILABLE_ON_PLATFORM

8.2 FreeBSD & Android 构建

Bun 现在支持编译到 FreeBSD 和 Android 平台,扩大了嵌入式和 IoT 场景的覆盖范围。

8.3 共享 SSL_CTX 缓存

多个 HTTPS 连接现在可以共享 SSL_CTX,减少了 TLS 握手的重复初始化开销。


九、从 Node.js 迁移到 Bun:生产级检查清单

基于 Bun 1.3.14 的功能覆盖,以下是迁移评估框架:

9.1 可以直接迁移的场景

场景置信度说明
Express/Koa/Hono API 服务⭐⭐⭐⭐⭐Bun.serve() 完全兼容
图片处理服务⭐⭐⭐⭐⭐Bun.Image 替代 sharp,更快
WebSocket 服务⭐⭐⭐⭐Bun.serve() 原生支持 upgrade
SQLite 应用⭐⭐⭐⭐⭐内置 bun:sqlite,支持 3.51.2
CLI 工具⭐⭐⭐⭐⭐启动快,内置 shell API
文件监听服务⭐⭐⭐⭐fs.watch() 重写后更可靠

9.2 需要注意的场景

场景风险建议
大量使用 native addons (N-API)⚠️ 中逐个测试兼容性
复杂 worker_threads 依赖⚠️ 中Bun 有 worker 支持但行为有细微差异
node:cluster 负载均衡⚠️ 中考虑用外部负载均衡替代
HTTP/3 生产部署⚠️ 高仍为实验性,等稳定后再用

9.3 迁移步骤

# 1. 安装 Bun
curl -fsSL https://bun.sh/install | bash

# 2. 在项目中安装依赖(生成 bun.lockb)
bun install

# 3. 运行测试
bun test

# 4. 启动服务
bun run src/index.ts

# 5. 性能对比
# - 启动时间:通常 5-20ms vs Node.js 50-200ms
# - 内存占用:通常低 30-50%
# - 请求吞吐:HTTP 服务通常高 2-4 倍

十、Bun 生态演进:从运行时到 AI 基础设施

10.1 加入 Anthropic 后的变化

2025 年 12 月,Bun 宣布加入 Anthropic。这对开发者的实际影响:

  1. Claude Code 使用 Bun 作为运行时 — Bun 成为 AI 编程工具链的核心
  2. 更大规模的性能优化投入 — Anthropic 的资源加速了 Bun 的迭代
  3. AI 场景的深度优化 — 包括 MCP server 支持、Python asyncio 互操作等

10.2 Bun 的独特定位

在 2026 年的 JS 运行时格局中:

特性Node.jsDenoBun
包生态兼容✅ 完全⚠️ npm 兼容层✅ 完全
内置打包器❌ 需 webpack
内置测试框架❌ 需 Jest
内置图片处理✅ Bun.Image
HTTP/3 服务端✅ (实验性)
HTTP/2 客户端✅ (实验性)
内置 SQLite
内置 S3
内置 Markdown
启动速度极快
AI 工具链支持✅ (Anthropic)

十一、总结与展望

Bun 1.3.14 标志着一个重要里程碑:Bun 不再只是「更快的 Node.js 替代品」,而是开始提供 Node.js 和 Deno 都没有的独特能力。

核心亮点回顾:

  1. Bun.Image — 内置图片处理,70× 元数据查询加速,比 sharp 快 20-38%,零 native 依赖
  2. Global Virtual Store — CI 暖安装 7.3× 加速,消除 macOS APFS clonefileat 瓶颈
  3. HTTP/3 — Bun.serve() 原生 QUIC 支持,静态路由比 HTTPS/1.1 快 2.7×
  4. HTTP/2 客户端 — fetch() 多路复用,连接合并,RFC 9113 安全加固
  5. fs.watch() 重写 — 修复多个长期 bug,Linux 新目录追踪
  6. --no-orphans — 彻底解决进程孤儿问题

对开发者的建议:

  • 新项目: 如果不依赖特定的 Node.js native addon,Bun 是 2026 年最值得尝试的 JS 运行时
  • 图片处理场景: 可以直接从 sharp 迁移到 Bun.Image,性能更好且零依赖
  • CI/CD: 启用 Global Virtual Store,将依赖安装时间从秒级降到毫秒级
  • HTTP/3: 保持关注但暂不上生产,等待稳定后再部署
  • 现有项目迁移: 优先迁移 API 服务和 CLI 工具,这两类兼容性最好

Bun 的节奏越来越快,每一次更新都在扩展「运行时」这个词的边界。当你发现一个工具不仅能运行代码,还能处理图片、服务 HTTP/3、监听文件变更、管理进程生命周期——它就已经超越了「运行时」的范畴,变成了开发基础设施

而这,可能只是开始。


本文基于 Bun v1.3.14 官方博客和源码分析,代码示例均经过验证。Bun 处于快速迭代中,部分 API 可能随版本变化,建议参考 bun.sh/blog 获取最新信息。

复制全文 生成海报 Bun JavaScript HTTP/3 Web开发 运行时

推荐文章

如何在 Vue 3 中使用 Vuex 4?
2024-11-17 04:57:52 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
MySQL 1364 错误解决办法
2024-11-19 05:07:59 +0800 CST
10个极其有用的前端库
2024-11-19 09:41:20 +0800 CST
为什么要放弃UUID作为MySQL主键?
2024-11-18 23:33:07 +0800 CST
什么是Vue实例(Vue Instance)?
2024-11-19 06:04:20 +0800 CST
PHP 的生成器,用过的都说好!
2024-11-18 04:43:02 +0800 CST
MySQL用命令行复制表的方法
2024-11-17 05:03:46 +0800 CST
Grid布局的简洁性和高效性
2024-11-18 03:48:02 +0800 CST
PHP中获取某个月份的天数
2024-11-18 11:28:47 +0800 CST
程序员茄子在线接单