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 支持的格式
| 格式 | macOS | Windows | Linux |
|---|---|---|---|
| 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.Image | sharp | 加速比 |
|---|---|---|---|
| metadata() | 0.004 ms | 0.28 ms | 70× |
| 1080p PNG → 400×400 JPEG | 28.6 ms | 39.5 ms | 1.38× |
| 1080p PNG → 800×600 WebP | 82.7 ms | 110.1 ms | 1.33× |
| 4K JPEG → 800×450 JPEG | 35.8 ms | 45.5 ms | 1.27× |
| 4K JPEG → 1920×1080 JPEG | 57.2 ms | 69.9 ms | 1.22× |
| 12MP JPEG → 1024×768 WebP | 138 ms | 165 ms | 1.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 hoisted | 823 ms | 478 ms | 1,387 |
| --linker isolated (之前) | 841 ms | 1,256 ms | 1,387 |
| --linker isolated + globalStore | 115 ms | 94 ms | 0 |
暖安装从 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 的几个根本性瓶颈:
- 连接建立慢: TCP 需要三次握手,TLS 又需要额外往返。QUIC 将传输和加密握手合并为一次往返。
- 队头阻塞 (Head-of-Line Blocking): TCP 上一个丢包会阻塞所有后续流。QUIC 的流独立,互不影响。
- 连接迁移: 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/3 | HTTPS/1.1 | HTTP/1.1 |
|---|---|---|---|
| 静态路由 (routes) | 509,135 req/s | 189,130 req/s | 239,476 req/s |
| 动态 fetch handler | 283,485 req/s | 142,323 req/s | 171,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 的),然后:
- 递归 SIGKILL 所有子孙进程
- 使用
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 平台差异
| 特性 | POSIX | Windows |
|---|---|---|
| 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。这对开发者的实际影响:
- Claude Code 使用 Bun 作为运行时 — Bun 成为 AI 编程工具链的核心
- 更大规模的性能优化投入 — Anthropic 的资源加速了 Bun 的迭代
- AI 场景的深度优化 — 包括 MCP server 支持、Python asyncio 互操作等
10.2 Bun 的独特定位
在 2026 年的 JS 运行时格局中:
| 特性 | Node.js | Deno | Bun |
|---|---|---|---|
| 包生态兼容 | ✅ 完全 | ⚠️ npm 兼容层 | ✅ 完全 |
| 内置打包器 | ❌ 需 webpack | ✅ | ✅ |
| 内置测试框架 | ❌ 需 Jest | ✅ | ✅ |
| 内置图片处理 | ❌ | ❌ | ✅ Bun.Image |
| HTTP/3 服务端 | ❌ | ❌ | ✅ (实验性) |
| HTTP/2 客户端 | ❌ | ❌ | ✅ (实验性) |
| 内置 SQLite | ❌ | ❌ | ✅ |
| 内置 S3 | ❌ | ❌ | ✅ |
| 内置 Markdown | ❌ | ❌ | ✅ |
| 启动速度 | 中 | 快 | 极快 |
| AI 工具链支持 | — | — | ✅ (Anthropic) |
十一、总结与展望
Bun 1.3.14 标志着一个重要里程碑:Bun 不再只是「更快的 Node.js 替代品」,而是开始提供 Node.js 和 Deno 都没有的独特能力。
核心亮点回顾:
- Bun.Image — 内置图片处理,70× 元数据查询加速,比 sharp 快 20-38%,零 native 依赖
- Global Virtual Store — CI 暖安装 7.3× 加速,消除 macOS APFS clonefileat 瓶颈
- HTTP/3 — Bun.serve() 原生 QUIC 支持,静态路由比 HTTPS/1.1 快 2.7×
- HTTP/2 客户端 — fetch() 多路复用,连接合并,RFC 9113 安全加固
- fs.watch() 重写 — 修复多个长期 bug,Linux 新目录追踪
- --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 获取最新信息。