Bun v1.4 更新总结:把浏览器、图片、定时任务和工程工具都装进一个运行时

我们平时开发 JavaScript 项目,图片处理要装 sharp,浏览器自动化要装 Playwright,定时任务要装 node-cron,终端交互还要装 node-pty

Bun 1.4 继续做一件事,把这些常用能力直接放进 Bun 二进制里。

官方更新文章:bun.sh/blog/bun-v1...

先说结论。

Bun 1.4 最值得关注的不是某一个 API,而是它开始更像一个完整的 JavaScript 工具箱。

这次更新覆盖运行时、HTTP 服务、包管理器、测试、构建器、性能、安全和平台支持。

配置解析、日志格式和压缩这类小依赖,也开始有了内置替代。

文章中的一部分功能在 Bun 1.3.x 已经出现,Bun 1.4 继续增强了它们。

所以文中会标出 1.4 新增1.4 增强,避免把版本时间线混在一起。

功能总览

分类 功能 我的判断
运行时 Bun.Image 有用,适合图片上传和缩略图
运行时 Bun.WebView 有趣又有用,适合浏览器自动化
运行时 Bun.markdown 有用,适合 Markdown 渲染
运行时 Bun.cron 有用,适合定时任务
运行时 Bun.Terminal 有用,适合终端和 PTY 工具
运行时 JSON5、JSONL、JSONC、XML、TOML 有用,配置和日志解析不用装包
运行时 压缩流、textStream()Bun.Archive 有用,流式压缩与 tar 处理
运行时 Bun.serve() 静态目录 重要,减少 Web 服务依赖
HTTP 请求压缩 有用,适合大 JSON 上传
HTTP 代理鉴权和 TLS 会话复用 有用,内网代理和高频连接
HTTP 流式响应背压 重要,慢客户端不再拖垮内存
HTTP HTTP/2 和 HTTP/3 有趣,目前仍是实验功能
工程 bun run --parallel 有用,适合 Monorepo
工程 全局虚拟存储 重要,适合 CI 和多项目开发
工程 bun pm diff 有用,升级依赖前先看差异
工程 bun update 和嵌套 overrides 有用,依赖治理更精细
测试 并行、隔离、分片、受影响测试 重要,适合大型测试集
构建 React Compiler 重要,减少手动优化
构建 bun:bundle 特性开关 有用,构建时移除死代码
构建 内存文件和单文件 HTML 有趣,适合代码生成和离线分发
构建 ESM 字节码和更快的代码分割 有用,单文件分发更完整
性能 Rust 重写和 JavaScriptCore 优化 重要,生产资源消耗下降
生产 memoryPressure 低内存通知 有用,OOM 前主动释放内存
安全 TLS、HTTP 解析和 tar 包加固 重要,建议所有项目升级
平台 Windows ARM64、FreeBSD、实验性 Android 有用,平台覆盖更广

开始升级

先升级到 Bun 1.4。

bash 复制代码
bun upgrade
bun --version

然后在项目根目录重新安装依赖。

bash 复制代码
bun install

如果项目使用了原生扩展、Monorepo 或自定义 TLS,建议先看文末的升级注意事项。

Bun.Image:图片处理内置化

版本:1.3.14 提供,1.4 收录并继续完善

Bun.Image 是 Bun 内置的图片处理库。

它可以完成解码、缩放、旋转和重新编码,支持 JPEG、PNG、WebP、GIF 和 BMP。

在 macOS 和 Windows 上,HEIC、AVIF 和 TIFF 也可以使用。

以前处理缩略图,通常要安装 sharp 和对应的 native addon。

现在可以直接写在 Bun 项目里。

ts 复制代码
await Bun.file("photo.jpg").image().resize(1024, 1024, { fit: "inside" }).rotate(90).webp({ quality: 85 }).write("thumb.webp");

这段代码会读取 photo.jpg,把图片限制在 1024×1024 以内,旋转 90 度,再输出为质量为 85 的 WebP。

处理上传文件时,也可以直接返回 HTTP 响应。

ts 复制代码
Bun.serve({
    async fetch(req) {
        const form = await req.formData();
        const upload = form.get("file");

        if (!(upload instanceof File)) {
            return new Response("file is required", { status: 400 });
        }

        const image = new Bun.Image(upload).resize(200).jpeg();
        return new Response(image, {
            headers: { "Content-Type": "image/jpeg" }
        });
    }
});

官方文章给出的基准是,1080p PNG 缩放成 400×400 JPEG 时,Bun.Imagesharp 快 1.38 倍。

这个数字是官方基准,不代表所有图片和机器都能得到相同结果。

Bun.WebView:不用 Playwright 也能驱动浏览器

版本:1.3.12 提供,1.4 增强

Bun.WebView 是内置的无头浏览器自动化 API。

它能打开网页、点击元素、执行 JavaScript、截图和发送 CDP 命令。

最有意思的地方是,点击和滚动是真实的用户输入,页面能观察到 event.isTrusted === true

ts 复制代码
await using view = new Bun.WebView({ width: 800, height: 600 });

await view.navigate("https://bun.sh");
await view.click("a[href='/docs']");

const title = await view.evaluate("document.title");
console.log(title);

await Bun.write("page.png", await view.screenshot());

macOS 默认使用系统 WebKit。

macOS、Linux 和 Windows 也可以驱动已经安装的 Chrome、Chromium 或 Edge。

因此在 Windows 和 Linux 上使用 Chrome 路径时,机器上需要先安装对应浏览器。

它适合做网页截图、后台巡检、自动化登录后的页面操作和简单的端到端脚本。

Bun.markdown:Markdown 解析器直接可用

版本:1.3.8 提供,1.4 增强

Bun.markdown 可以把 Markdown 转成 HTML、React 元素或终端 ANSI 文本。

最简单的用法如下。

ts 复制代码
const markdown = "# Hello **world**";
const html = Bun.markdown.html(markdown);

console.log(html);
// <h1>Hello <strong>world</strong></h1>

在 React 页面中,可以直接返回 React 元素。

tsx 复制代码
export default function ReadmePage({ readme }: { readme: string }) {
    return Bun.markdown.react(readme);
}

它支持 GFM 表格、删除线、任务列表和自动链接。

它还支持自定义渲染器,例如把标题变成终端的粗体和下划线。

ts 复制代码
const output = Bun.markdown.render("# Hello\n\n**bold**", {
    heading: (children) => `\x1b[1;4m${children}\x1b[0m\n`,
    paragraph: (children) => `${children}\n`,
    strong: (children) => `\x1b[1m${children}\x1b[22m`
});

process.stdout.write(output);

有一个安全边界必须记住。

Bun.markdown.html() 不会自动清洗 HTML。

原始 HTML、事件属性和 javascript: 链接可能原样输出。

所以渲染不可信用户内容时,要先使用 HTML sanitizer,再把结果放进网页。

Bun.cron:把定时任务交给操作系统

版本:1.3.11 提供,1.4 增强

Bun.cron 可以注册操作系统级定时任务。

Linux 使用 crontab,macOS 使用 launchd,Windows 使用 Task Scheduler。

ts 复制代码
await Bun.cron("./worker.ts", "30 2 * * MON", "weekly-report");

worker.ts 可以导出 scheduled 处理器。

ts 复制代码
export default {
    async scheduled(controller: { cron: string; scheduledTime: number }) {
        console.log(controller.cron, controller.scheduledTime);
        await generateWeeklyReport();
    }
};

async function generateWeeklyReport() {
    console.log("report generated");
}

如果只想在当前 Bun 进程中运行,也可以传函数。

ts 复制代码
using job = Bun.cron("*/5 * * * *", async () => {
    await cleanupTempFiles();
});

job.unref();

文件形式依赖操作系统的任务调度权限。

函数形式只运行在当前事件循环里,不会注册系统任务。

Bun 1.4 默认使用本地时间,并增加了 { tz } 选项指定时区。

跨地区部署时,建议明确写出时区,不要依赖服务器默认时区。

Bun.Terminal:内置伪终端

版本:1.3.5 提供,1.4 增强

Bun.Terminal 是内置的 PTY,也就是可以运行交互式命令的伪终端。

它可以驱动 bashvimhtop,不需要额外安装 node-pty

ts 复制代码
const proc = Bun.spawn(["bash"], {
    terminal: {
        cols: 80,
        rows: 24,
        data(_terminal, data) {
            process.stdout.write(data);
        }
    }
});

proc.terminal.write("echo Hello from PTY!\n");

Linux、macOS 和 Windows 都支持这个 API。

示例使用 bash,Windows 上需要换成机器上实际存在的 Shell。

它适合做终端面板、远程命令执行界面和交互式 CLI 包装器。

内置解析器:JSON5、JSONL、JSONC、XML 和 TOML

版本:1.3.x 起陆续内置,1.4 继续增强

配置文件和日志解析,是另一批高频安装的依赖。

Bun 把它们也做成了内置 API。

Bun.JSON5

Bun.JSON5 用来解析和生成 JSON5,也就是允许注释、尾逗号和不加引号的键名的那种 JSON。

ts 复制代码
const config = Bun.JSON5.parse(`{
  // 数据库配置
  host: "localhost",
  port: 3306,
}`);

console.log(config.port);
// 3306

.json5 文件也可以直接 import。

ts 复制代码
import config from "./config.json5";

它替代的是 json5 这个包。

Bun.JSONL

Bun.JSONL 用来处理按行分隔的 JSON,也就是每一行一个 JSON 对象的格式。

日志文件和大规模数据导出经常用它。

ts 复制代码
const events = Bun.JSONL.parse('{"type":"login"}\n{"type":"logout"}\n');

console.log(events.length);
// 2

处理流式数据时,可以用 parseChunk() 一块一块地解析,不用等整个文件读完。

它替代的是 ndjson

Bun.JSONC

Bun.JSONC.parse() 解析带注释和尾逗号的 JSON,tsconfig.json 用的就是这种格式。

ts 复制代码
const tsconfig = Bun.JSONC.parse(`{
  "compilerOptions": {
    "strict": true, // 严格模式
  },
}`);

console.log(tsconfig.compilerOptions.strict);
// true

它替代的是 jsonc-parser

Bun.XML

Bun.XML 是 SIMD 加速的 XML 解析器和序列化器。

.xml 文件可以直接 import。

ts 复制代码
import sitemap from "./sitemap.xml";

注意 1.4 的一个行为变化,.xml 导入现在返回解析后的对象,不再是文件路径。

需要路径时,要显式改用 --loader .xml:file

它替代的是 fast-xml-parserxml2js

Bun.TOML

Bun.TOML 支持 TOML v1.1.0,通过了 toml-test 全部 708 个用例。

1.4 新增了 stringify(),可以把对象序列化回 TOML。

ts 复制代码
const toml = Bun.TOML.stringify({
    name: "my-app",
    scripts: { dev: "bun run dev.ts" }
});

console.log(toml);

它替代的是 @iarna/toml

压缩流、textStream() 和 Bun.Archive

版本:1.3.x 起陆续内置,1.4 继续增强

CompressionStreamDecompressionStream

Web 标准的压缩流在 Bun 里可以直接用,支持 gzipdeflatedeflate-rawbrotlizstd

ts 复制代码
const compressed = new Blob(["hello world".repeat(1000)]).stream().pipeThrough(new CompressionStream("gzip"));

await Bun.write("data.gz", compressed);

解压反过来用 DecompressionStream 就行。

它适合流式地压缩和解压数据,和 fetch()compress 选项是两回事。

Response.textStream()

textStream() 返回一个按 UTF-8 解码好的字符串流,读取大文本响应时不用自己拼 TextDecoder

ts 复制代码
const response = await fetch("https://example.com/big.txt");

for await (const chunk of response.textStream()) {
    process.stdout.write(chunk);
}

Bun.Archive

Bun.Archive 用来创建和解压 tar 包,整个过程在主线程之外执行,不会卡住事件循环。

构建产物打包、缓存归档这类场景,不再需要安装 tar 这个依赖。

具体 API 可以看官方文档的 Archive 一节。

bun run --parallel:并行跑多个脚本

版本:1.3.9 提供,1.4 增强

以前经常要安装 concurrentlynpm-run-all 才能并行跑脚本。

Bun 1.4 可以直接这样写。

bash 复制代码
bun run --parallel build test

也可以使用脚本名通配符。

bash 复制代码
bun run --parallel "build:*"

在 Monorepo 中,可以让所有 workspace 同时执行 build

bash 复制代码
bun run --parallel --filter '*' build

某个 workspace 失败后仍然继续执行其他任务。

bash 复制代码
bun run --parallel --no-exit-on-error --filter '*' test

输出会自动带上脚本名,多个任务同时输出时更容易定位来源。

需要注意,脚本如果共用端口、临时目录或数据库,仍然要自己处理资源冲突。

Bun.serve():直接服务静态目录

版本:1.4 新增

Bun.serve() 的路由现在可以直接映射一个目录。

ts 复制代码
Bun.serve({
    port: 3000,
    routes: {
        "/static/*": { dir: "./public" }
    }
});

public/index.html 会作为目录首页返回。

Bun 还会自动处理 Content-TypeETagLast-Modified304Range

这意味着很多项目不再需要单独安装 express.staticserve-staticsirv

视频和大文件下载也可以直接利用断点范围请求。

ts 复制代码
Bun.serve({
    port: 3000,
    routes: {
        "/video.mp4": new Response(Bun.file("./video.mp4"))
    }
});

测试范围请求。

bash 复制代码
curl -H "Range: bytes=0-1023" http://localhost:3000/video.mp4

正常情况下会返回 206 Partial Content

Bun 会规范化静态文件路径,Linux 上还会使用 openat2 限制符号链接越出目标目录。

顺带一提流式响应的背压处理(1.4 新增)。

当客户端接收很慢、socket 发送缓冲区被填满时,Bun 会暂停流的 pull(),等缓冲区排空再继续。

Bun 1.3 的行为是把发不出去的数据一直堆在内存里,慢客户端足够久之后,进程会被拖垮。

1.4 之后,大文件和流式接口服务慢客户端时稳定了很多。

fetch() 请求压缩:上传大 JSON 更省流量

版本:1.4 新增

fetch() 新增了 compress 选项,可以在发送请求前压缩请求体。

支持 gzipdeflatebrzstd

ts 复制代码
const response = await fetch("https://api.example.com/upload", {
    method: "POST",
    body: JSON.stringify({ items: Array(10_000).fill({ ok: true }) }),
    compress: { encoding: "gzip", level: 6 }
});

console.log(response.status);

Bun 会自动设置 Content-Encoding,并让 Content-Length 反映压缩后的大小。

字符串、ArrayBuffer、TypedArray 和 Blob 等缓冲体会自动压缩。

流式请求体会原样发送,不会自动压缩。

服务端必须支持对应的 Content-Encoding

fetch() 的代理选项也增强了(1.3.4 提供)。

proxy 现在可以传对象,直接给代理服务器发自定义请求头,比如 Proxy-Authorization

ts 复制代码
await fetch("https://api.example.com/data", {
    proxy: {
        url: "http://proxy.example.com:8080",
        headers: { "Proxy-Authorization": "Bearer token" }
    }
});

公司内网走认证代理的场景,以前要自己拼头,现在一个对象就够了。

TLS 会话复用也是 1.4 的新变化。

对同一个来源的第二次冷连接,可以复用缓存的会话,1 个往返就能恢复连接,不用再走完整的握手和证书链校验。

高频短连接的服务,比如定时调外部 API 的任务,连接开销会小不少。

HTTP/2 和 HTTP/3:有趣,但先别急着上生产

版本:1.3.14 提供,1.4 增强

fetch() 可以显式指定 HTTP/2 或 HTTP/3。

ts 复制代码
const response = await fetch("https://example.com", {
    protocol: "http3"
});

console.log(response.status);

HTTP/2 下,同一个来源的并发请求可以复用一条连接。

Bun.serve() 也可以开启 HTTP/3。

ts 复制代码
Bun.serve({
    port: 443,
    tls: {
        cert: Bun.file("./cert.pem"),
        key: Bun.file("./key.pem")
    },
    http3: true,
    fetch() {
        return new Response("hello");
    }
});

HTTP/3 会额外监听同端口的 UDP。

官方明确把这两个能力标成实验性功能。

Bun.serve() 的 HTTP/3 还不支持完整的 0-RTT 恢复,HTTP/3 上的 server.upgrade() 也会返回 false

因此可以用于实验和压测,不建议现在直接作为生产默认配置。

bun:ffi:更快的原生函数调用

版本:1.4 新增引擎级实现

bun:ffi 使用 JavaScriptCore 内置的 FFI 能力调用 C 函数。

官方基准中,部分空调用约快 3 倍,CString 场景约快 3.8 倍。

下面是 API 形状示例。

ts 复制代码
import { dlopen } from "bun:ffi";

const { symbols } = dlopen("libhash.so", {
    hash: {
        args: ["buffer", "buffer_length"],
        returns: "cstring"
    }
});

const data = new Uint8Array([1, 2, 3]);
const digest = symbols.hash(data, data);

console.log(digest);

libhash.sohash 只是示例占位符。

真实运行时需要准备和当前操作系统、架构及 ABI 匹配的动态库。

Bun 1.4 中,returns: "cstring" 返回普通字符串,C 的 NULL 指针返回 null

如果使用 CString 管理内存,需要保留原始指针,因为 CString 不再提供 .ptr

全局虚拟存储:重复安装依赖少复制几次

版本:1.3.14 提供,1.4 收录并继续优化

选用 isolated linker 后,全局虚拟存储才会启用。

依赖包只提取一次到 Bun 的全局缓存,再通过符号链接放进每个项目的 node_modules/.bun/ 目录。

这是需要主动开启的选择,现有项目升级 Bun 后不会自动切换。

bash 复制代码
bun install --linker=isolated

也可以在 bunfig.toml 中固定配置。

toml 复制代码
[install]
linker = "isolated"

它适合本机有很多项目,或者 CI 缓存比较稳定的场景。

官方文章称,锁文件存在、缓存预热的 CI 场景中,1400 个包的安装最多可以快 7 倍。

新建 Monorepo 默认使用 isolated linker。

已有锁文件不会因为升级 Bun 就自动改变目录布局。

bun pm diff:升级依赖前先看代码差异

版本:1.4 新增

升级依赖之前,最怕的是不知道新版本到底改了什么。

bun pm diff 会对比两个包版本,并提示文件变化、安装脚本和危险 Node.js 模块导入。

bash 复制代码
bun pm diff react
bun pm diff react@18.2.0 19.0.0
bun pm diff ./vendored-pkg pkg@2.1.0

还可以只看匹配的文件。

bash 复制代码
bun pm diff react-dom@18.2.0 18.3.1 "*.min.js"

它适合作为升级依赖前的人工检查步骤。

bun audit fix:修复依赖漏洞

版本:1.4 新增

bash 复制代码
bun audit fix

这个命令会尝试升级存在漏洞的依赖并重新安装。

如果需要跨越 major 版本,可以使用 --latest

bash 复制代码
bun audit fix --latest
bun audit fix --dry-run

--dry-run 只展示准备修改的内容,不立即写入依赖。

如果上游依赖的版本范围阻止升级,Bun 可能只能修复一部分漏洞。

所以命令成功执行不等于所有漏洞都已经解决,仍然要检查最终报告。

bun dedupebun prune 和许可证清单

版本:1.4 新增

bun dedupe 会合并满足相同 semver 范围的重复依赖。

bash 复制代码
bun dedupe
bun dedupe --check

--check 适合放进 CI,发现可以去重的依赖时让检查失败。

bun prune 用来删除锁文件中不存在的依赖。

bash 复制代码
bun prune
bun prune --production

典型容器流程如下。

dockerfile 复制代码
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
RUN bun prune --production

这样构建阶段保留开发依赖,发布镜像只留下生产依赖。

查看生产依赖的许可证信息。

bash 复制代码
bun pm licenses --prod --json > licenses.json

Workspace filter 和 catalog

版本:1.4 新增或增强

可以只给某个 workspace 添加依赖。

bash 复制代码
bun add zod --filter api

也可以运行某个 workspace 以及它的依赖。

bash 复制代码
bun run --filter 'web...' build

web... 表示 web 以及它依赖的 workspace。

...web 表示依赖 web 的 workspace。

catalog 用来统一管理 Monorepo 中的公共依赖版本。

bash 复制代码
bun add react --catalog

workspace 的 package.json 可以引用 catalog 版本。

json 复制代码
{
    "dependencies": {
        "react": "catalog:"
    }
}

这样升级 React 时只需要维护一份版本号。

bun update 和嵌套 overrides

版本:1.4 新增

bun update 现在也更新传递依赖

以前 bun update 只更新 package.json 里直接声明的依赖。

1.4 之后,依赖的依赖也会一起更新。

bash 复制代码
bun update

指定包名时,会更新这个包在整棵依赖树里的所有副本,包括其他 workspace 里的副本。

bash 复制代码
bun update zod
bun update '@types/*' --latest

还可以按模式批量更新,比如一次更新所有类型包到最新版。

一个小变化要留意,更新的包当前不被任何地方依赖时,Bun 现在会报错退出,而不是把它当成新依赖加进来。

嵌套 overrides

以前想覆盖某个依赖的依赖,只能全局替换,影响整棵依赖树。

1.4 支持嵌套覆盖,只动指定父依赖下面的那一份。

json 复制代码
{
    "overrides": {
        "express": {
            "qs": "6.13.0"
        },
        "lodash@<4.17.21": "4.17.21"
    }
}

这段配置的意思是,只把 express 内部的 qs 固定到 6.13.0,其他包用的 qs 不受影响。

lodash@<4.17.21 表示只覆盖低于这个版本的 lodash

npm 的嵌套写法、Yarn 的 a/b 写法和 pnpm 的 a>b 写法都支持。

要注意,使用嵌套或版本限定的 override 后,锁文件会变成 lockfileVersion: 3,旧版 Bun 读不了。

测试能力:并行、隔离、分片和只跑受影响测试

bun test --parallel

版本:1.3.13 提供,1.4 增强

bash 复制代码
bun test --parallel
bun test --parallel=4 --isolate

Bun 会启动多个 worker,把测试文件分配给空闲 worker。

覆盖率和 JUnit 报告会合并。

并行测试时要注意端口、数据库和临时目录的资源争用。

bun test --isolate

版本:1.3.13 提供,1.4 增强

--isolate 让每个测试文件跑在一个全新的 JavaScript global 里,但仍然在同一个进程内。

这和 Jest、Vitest 的默认行为一致。

专门解决一类经典问题,单个文件跑能过,整个套件一起跑就挂。

bash 复制代码
bun test --isolate

两个文件之间,Bun 会做这些清理。

  • 新建 globalThis,上一个文件挂上去的属性和补丁全部消失。
  • 清空 ESM 和 CommonJS 模块注册表,每个文件重新执行自己的 import。
  • 关闭上个文件留下的服务器、socket、文件监听器和子进程,取消定时器。
  • 重新执行 --preload 脚本。

转译产物和字节码在进程级缓存,第二个文件导入同一个模块时,只需要重新执行模块顶层代码,不用重新读文件和转译。

--parallel 默认隐含 --isolate,不想隔离时可以用 --no-isolate 关掉。

bun test --shard

版本:1.3.13 提供,1.4 增强

CI 有多个 runner 时,可以把测试切成多个分片。

bash 复制代码
bun test --shard=1/3
bun test --shard=2/3
bun test --shard=3/3

Bun 会先排序测试文件,再按轮询方式分配到各个分片。

它还可以和 --parallel 组合,让每台 CI 机器继续使用多个 worker。

--timings

版本:1.4 新增

先记录每个测试文件的耗时。

bash 复制代码
bun test --timings=timings.json --update-timings

下次分片时,Bun 会尽量按耗时而不是文件数量均衡。

bash 复制代码
bun test --shard=1/3 --timings=timings.json

首次运行没有历史耗时,后续运行才会越来越准确。

--changed

版本:1.3.13 提供,1.4 增强

bash 复制代码
bun test --changed
bun test --changed=main

Bun 会查看 Git 改动,再沿着 import 关系反向找到受影响的测试。

这比只运行改动文件对应的测试更实用。

动态导入或工具无法分析的依赖关系,可能不会被完整捕捉。

retryrepeats

版本:1.3.3 提供,1.4 增强

对偶发失败的测试,可以设置有限重试次数。

ts 复制代码
test(
    "unstable service",
    async () => {
        expect(await checkService()).toBe(true);
    },
    { retry: 5 }
);

想验证测试稳定性,可以强制重复执行。

ts 复制代码
test(
    "stress check",
    () => {
        expect(runCheck()).toBe(true);
    },
    { repeats: 20 }
);

也可以给整个测试套件设一个默认重试次数。

bash 复制代码
bun test --retry 5

重试只是降低偶发失败对 CI 的影响,不是修复 flaky 测试的替代品。

构建能力:React Compiler 和构建期特性开关

内置 React Compiler

版本:1.4 新增

Bun 可以在构建时自动优化 React 组件和 Hooks。

bash 复制代码
bun build ./src/index.tsx --outdir ./dist --react-compiler

API 方式如下。

ts 复制代码
await Bun.build({
    entrypoints: ["./src/index.tsx"],
    outdir: "./dist",
    reactCompiler: true
});

官方基准中,大型 React 项目开启 Compiler 后只增加约 71 毫秒构建时间。

这个结果是官方测试项目的结果,不应当当成所有项目的固定耗时。

Barrel import 优化

版本:1.3.10 提供,1.4 增强

导入 antd 这类 barrel 包时,Bun 会尝试跳过没有被使用的模块。

ts 复制代码
import { Button } from "antd";

如果包正确声明了 sideEffects: false,优化可以自动启用。

也可以显式配置。

ts 复制代码
await Bun.build({
    entrypoints: ["./src/index.tsx"],
    outdir: "./dist",
    optimizeImports: ["antd", "@mui/material"]
});

只有在确认包没有依赖导入副作用时,才应该设置 sideEffects: false

bun:bundle:构建期特性开关

版本:1.3.5 提供,1.4 继续完善

bun:bundle 可以在构建时决定某段代码是否存在。

ts 复制代码
import { feature } from "bun:bundle";

if (feature("SUPER_SECRET")) {
    console.log("secret feature enabled");
}

构建时传入特性名。

bash 复制代码
bun build --feature=SUPER_SECRET ./src/index.ts

没有启用的分支会被当成死代码移除。

它是构建时开关,不是运行时环境变量。

内存文件、单文件 HTML 和资源嵌入

Bun.build() 使用内存文件

版本:1.3.6 提供,1.4 继续完善

代码生成器或测试工具不一定要先把文件写到磁盘。

ts 复制代码
const result = await Bun.build({
    entrypoints: ["/app/index.ts"],
    files: {
        "/app/index.ts": `import { greet } from "./greet.ts";
console.log(greet("World"));`,
        "/app/greet.ts": `export function greet(name: string) {
  return "Hello, " + name + "!";
}`
    }
});

console.log(result.success);

files 的值可以是字符串、Blob 或 TypedArray。

单文件 HTML

版本:1.3.10 提供,1.4 增强

bash 复制代码
bun build ./index.html --compile --target=browser --outdir=dist

生成的 HTML 会把脚本、样式和资源都内联。

它可以不依赖 Web 服务器,直接用 file:// 打开。

这适合离线演示、一次性工具和交付给客户的单页文件。

构建分析报告

版本:1.3.6 提供,1.4 增强

bash 复制代码
bun build ./src/index.ts --outdir ./dist --metafile-md=./dist/meta.md

也可以同时输出 JSON 格式的 metafile。

bash 复制代码
bun build ./src/index.ts \
  --outdir ./dist \
  --metafile=./dist/meta.json \
  --metafile-md=./dist/meta.md

Markdown 报告会列出最大输入、入口点、依赖链和体积信息。

排查打包体积时,可以先看这份报告再决定是否拆包。

--asset:把静态资源嵌入可执行文件

版本:1.4 新增

bash 复制代码
bun build ./build/index.js \
  --compile \
  --asset ./build/client \
  --asset ./build/prerendered \
  --outfile server

资源会被嵌入编译后的可执行文件,并通过 /$bunfs/ 目录树访问。

这个能力适合把前端静态文件和后端服务打成一个可分发文件。

它需要配合 --compile,嵌入资源不是外部可写目录。

ESM 字节码编译

版本:1.3.9 提供,1.4 增强

--bytecode 现在支持 ES modules。

bash 复制代码
bun build ./app.ts --compile --bytecode --format=esm

--bytecode --format=esm 必须配合 --compile 使用。

开启后,top-level await、import.meta、动态 import 和代码分割都可以进入字节码编译的二进制。

以前 --bytecode 会强制输出 CommonJS,现在不用妥协了。

代码分割大幅提速

版本:1.4 新增

代码分割的可达性遍历改成了广度优先,复杂度变成 O(V+E)

官方基准里,2 万个模块的菱形依赖图,链接时间从 4.65 秒降到 320 毫秒。

几千个模块的线性 import 链也不再撑爆调用栈。

这是构建器内部的改进,不需要改任何配置,升级就生效。

开发诊断工具

版本:1.4 新增或增强

生成 CPU profile。

bash 复制代码
bun --cpu-prof app.ts
bun --cpu-prof-md app.ts

生成堆快照。

bash 复制代码
bun --heap-prof app.ts
bun --heap-prof-md app.ts

cpuprofile 可以用 Chrome DevTools 打开。

heapsnapshot 可以用来排查内存增长和大对象。

父进程退出时,自动终止孤儿子进程。

bash 复制代码
bun --no-orphans app.ts

CI 或生产启动脚本中,可以禁止自动加载 .env

bash 复制代码
bun --no-env-file app.ts

生产运行时和随手小工具

process.on("memoryPressure")

版本:1.3.x 起内置,1.4 继续完善

当操作系统内存吃紧时,它会通知 Bun,Bun 再在 process 上发出 memoryPressure 事件。

macOS、Linux 和 Windows 都支持。

可以利用它在系统杀掉进程之前主动释放内存。

ts 复制代码
process.on("memoryPressure", () => {
    cache.clear();
    pool.drainIdle();
});

常见的动作是清空缓存、关闭空闲连接、停掉暂时不用的 worker。

要说明的是,这是操作系统的低内存通知,不是内存配额保证,释放动作得靠业务代码自己实现。

bun replbun ./README.md

bun repl 是原生 REPL,带语法高亮、历史记录和 Tab 补全,也支持 -e-p 直接执行表达式。

bash 复制代码
bun repl
bun repl -p '"1+1: " + (1 + 1)'

还有一个有意思的小功能,直接运行 Markdown 文件。

bash 复制代码
bun ./README.md

Bun 会把 Markdown 渲染到终端,全程不启动虚拟机,看项目文档不用装 glow

其他一句话更新

  • URLPattern 这个 Web API 内置了,408 个 Web 平台测试通过,替代 path-to-regexp
  • Bun.sliceAnsi()Bun.wrapAnsi()Bun.stringWidth() 处理终端对齐,中文、emoji 和 ANSI 颜色码都算得准。
  • Bun.spawn({ cgroup }) 可以在 Linux 上把子进程放进 cgroup 再启动,做资源限制。

Rust 重写和性能变化

Bun 1.4 是 Bun 改用 Rust 重写后的首个版本。

官方文章称,Claude Code 已经使用 Rust 版本运行了数月,Prisma Compute 也已经基于它发布。

性能方面,官方报告了这些结果。

  • Claude Code 的 CPU p99 从 24% 降到 10%。
  • Claude Code 的 CPU p50 从 5.8% 降到 2.5%。
  • 小型 hello-world 服务的空闲 CPU 占用下降约 5 倍。
  • HTTP 服务内存占用下降约 13% 到 48%。
  • Windows 启动时间从 39.0 毫秒降到 15.5 毫秒。
  • Linux 启动时间从 10.9 毫秒降到 5.1 毫秒。
  • Linux 峰值内存从 33 MB 降到 14.6 MB。
  • new URL() 最多快 4.6 倍。
  • Buffer.from(str, "hex") 约快 8 倍。
  • base64url 解码最高约快 46 倍。
  • Promise 操作约快 1.5 到 2.4 倍。

这些都是文章中的基准结果。

实际收益会受到代码类型、数据规模、操作系统和硬件影响。

Node.js 兼容性继续提高

Bun 1.4 对 Node.js 26.3.0 测试套件新增了 1,517 个通过测试。

这说明兼容范围在扩大,但不代表 Bun 已经百分之百兼容 Node.js。

官方还特别提到 Playwright、Next.js 16、Vitest、OpenTelemetry 和 dd-trace 的兼容改进。

原生 addon 需要关注新的 ABI 版本。

ts 复制代码
console.log(process.versions.modules);
// 147

如果项目中有按 NODE_MODULE_VERSION 下载预编译 native addon 的依赖,需要确认它提供了 ABI 147 的构建产物。

安全增强

Bun 1.4 的安全变化不是锦上添花,而是升级理由之一。

fetch() 的证书校验回调有了更可靠的执行时机(1.4 新增)。

当你显式传入 tls: { checkServerIdentity } 回调时,它会在 TLS 握手完成后、请求写出前执行,重定向的每一跳也会再执行一次。

回调返回 Error 时,fetch() 直接拒绝,请求一个字节都不会发出去。

这个时机特别适合证书锁定,也就是只信任特定指纹的证书。

ts 复制代码
const PINNED = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";

await fetch("https://api.example.com/upload", {
    method: "POST",
    body: secretPayload,
    tls: {
        checkServerIdentity(_hostname, cert) {
            if (cert.fingerprint256 !== PINNED) {
                return new Error("pin mismatch");
            }
        }
    }
});

其他几项安全收紧分布在 1.3.x 和 1.4 之间。

Bun.connect()Bun.listen() 在开启 requestCert 时,默认执行 rejectUnauthorized(1.4 新增)。

tls.connect() 默认用 host 作为证书校验的服务器名(1.3.13)。

Redis 的 rediss:// 连接默认校验主机名(1.3.14)。

Bun.serve() 会拒绝畸形 HTTP framing(1.3.4)。

tar 包中试图越出解压目录的路径会被跳过(1.3.6)。

官方还提醒,少数在 1.3 上能连上的连接,升级后会变成证书校验错误,这是预期的收紧行为。

使用自签名证书或私有 CA 时,要显式提供 CA。

ts 复制代码
await fetch("https://internal.example.com", {
    tls: {
        ca: [Bun.file("./internal-ca.pem")]
    }
});

只有在明确知道风险时,才应该关闭证书验证。

ts 复制代码
await fetch("https://localhost:8443", {
    tls: {
        rejectUnauthorized: false
    }
});

生产环境不要把 rejectUnauthorized: false 当成常规修复方案。

平台支持

Bun 1.4 增加了 Windows ARM64 原生构建。

Surface、Snapdragon X 和 Ampere 等 ARM64 Windows 设备可以直接运行原生 Bun。

FreeBSD 14.3 及以上提供 x86_64 和 aarch64 原生构建。

Android aarch64 和 x64 仍然是实验性支持。

Linux 的最低 glibc 版本降到 2.17,最低内核版本为 3.10。

Bun 还开始为 TypeScript 7 做准备。

Windows 上的高精度定时器也得到改善,1 毫秒定时器现在大约 1.4 毫秒触发,而不是过去的 15.5 毫秒左右。

升级时最容易踩到的变化

writeHeader() 改成 writeHead()

旧代码。

ts 复制代码
res.writeHeader(200, { "Content-Type": "text/plain" });

新代码。

ts 复制代码
res.writeHead(200, { "Content-Type": "text/plain" });

node 身份运行时不再自动加载 .env

这个变化只影响 Bun 以 node 身份运行的场景,包括 bun --bunbunx --bun 和把 Bun 软链接成 node 这三种情况。

这些方式现在和 Node.js 一样,不再自动读取 .env 文件。

如果脚本依赖 .env,要显式传入文件。

bash 复制代码
node --env-file=.env ./check.js

直接用 bun ./check.js 运行脚本时,.env 仍然会自动加载,行为没有变化。

新 Monorepo 默认使用 isolated linker

如果必须保持旧的 hoisted 布局,可以显式写入配置。

toml 复制代码
[install]
linker = "hoisted"

YAML 解析遵循 YAML 1.2

yesnoonoff 会被解析成字符串,而不是布尔值。

GitHub Actions 中的 on: 也会按字符串处理。

TOML 和 bunfig.toml 更严格

未加引号的非法字符串、键值之间缺少换行,以及超过 Number.MAX_SAFE_INTEGER 的整数都会抛出 SyntaxError

bun.lock 版本升级

Bun 1.4 的新锁文件是 lockfileVersion: 2

GitHub 和 tarball 依赖会记录 SHA-512 完整性信息,Git 依赖会检查路径穿越。

旧版 v0 和 v1 锁文件仍然可以读取,执行一次 bun install 就会迁移。

使用嵌套 override 或按版本限定的 override 时,锁文件会升级到 lockfileVersion: 3,旧版 Bun 无法读取。

编译后的可执行文件不再乱读运行目录配置

bun build --compile 生成的可执行文件不再自动读取运行目录中的 tsconfig.jsonpackage.json

.envbunfig.toml 仍然默认自动加载,不受这个变化影响。

如果确实需要旧行为,可以显式开启。

bash 复制代码
bun build --compile \
  --compile-autoload-tsconfig \
  --compile-autoload-package-json \
  ./src/index.ts

其他值得搜索的迁移点

  • 暂停模式下的 readable.read() 不带 size 时每次只返回一个 chunk,需要循环读到 null
  • fs.rmdir(path, { recursive: true }) 改用 fs.rm(path, { recursive: true, force: true })
  • RequestResponseclone() 要放在读取 body 之前。
  • fetch() 网络错误现在是 TypeError
  • Bun.cron() 默认使用本地时间,旧代码依赖 UTC 时要显式写 { tz: "UTC" }
  • Bun.$ 的插值不再自动展开 glob,需要改用明确的 glob API 或命令参数。
  • MariaDB JSON 字段会直接解析成对象,不要再重复调用 JSON.parse()

我怎么看

Bun 1.4 的重点不是单纯跑得更快,而是减少一个 JavaScript 项目需要安装和维护的外围工具。

图片处理、Markdown、定时任务、PTY、浏览器自动化、静态文件服务和配置解析,都开始有了内置方案。

对新项目来说,这会让依赖树更短,部署步骤更少。

对存量项目来说,真正需要认真评估的是兼容性和升级行为,而不是盲目替换所有依赖。

不知道你怎么看呢?欢迎在评论区留言。

感谢阅读,我是农村程序员,独立开发者,行业观察员,前端之虎陈随易。

相关推荐
人间凡尔赛2 小时前
2026 后端架构三驾马车:Wasm 容器上 K8s、存算分离与 AI 原生
后端·云原生·架构
凤山老林2 小时前
动态 i18n 体系落地:Spring Boot 多租户热加载与前后端协同实践
java·spring boot·后端·i18n
东风破_2 小时前
TypeScript 高级类型进阶:keyof、Exclude、Record 与类型组合思想
前端·后端·typescript
凤山老林3 小时前
数据库读写分离与动态路由实战:Spring Boot + ShardingSphere-JDBC 生产配置
数据库·spring boot·后端·分库分表·sharding-jdbc
DS随心转插件3 小时前
Grok生成的html怎么导出——AI导出鸭:大模型结构化输出的“最后一公里”工程化解构
前端·人工智能·ai·html·豆包·deepseek·ai导出鸭
demo007x3 小时前
DSH harness 中的上下文管理探秘
程序员·agent·deepseek
এ慕ོ冬℘゜3 小时前
使用 jQuery 动态渲染表格与状态切换
前端·javascript·jquery
Zane19943 小时前
调用了 async 函数却没执行?一文讲透协程、event loop 与 await
后端·python
Zane19943 小时前
HashMap 为什么要在长度16、容量必须是2的幂这些细节上较劲
java·后端