Bun 1.2.23发布:119个问题修复,性能飙升!

译自 bun.com/blog/bun-v1...

作者 Jarred Sumner · 2025年9月28日

升级 Bun

sh 复制代码
bun upgrade

bun install 中的 pnpm-lock.yaml 支持

bun install 现在会自动将 pnpm-lock.yamlpnpm-workspace.yaml 文件迁移为 bun.lock,并保留解析后的依赖版本。这包括对 pnpm 工作区和 catalog: 依赖的支持。

只需一条命令即可从 pnpm install 切换到 bun install

sh 复制代码
# 在 pnpm 项目中
bun install

bun install 旨在与现有的 Node.js 项目协同工作。这意味着您可以在继续使用 Node.js 作为运行时的同时,利用 bun install 惊人的性能。

如果您的项目使用 pnpm-workspace.yaml,您的 package.json 将被更新以包含 "workspaces": ["<每个包的名称>"]

使用 --cpu--os 标志过滤可选依赖

您现在可以使用 bun install 中新的 --cpu--os 标志来控制安装哪些特定于平台的可选依赖。当您为不同的目标环境(例如 Docker 容器或 CI/CD 流水线)安装依赖时,这非常有用。

您可以提供多个值来同时为多个目标安装依赖。

sh 复制代码
# 为 Linux ARM64 目标安装可选依赖
bun install --os linux --cpu arm64

# 为 x64 架构的 macOS 和 Linux 安装
bun install --os darwin --os linux --cpu x64

# 为所有支持的平台安装
bun install --os '*' --cpu '*'

Bun 的 Redis 客户端现在支持发布/订阅

Bun 内置的 RedisClient 现在支持发布/订阅消息模式。您可以使用新的 .subscribe() 方法来监听特定频道上的消息,并使用 .publish() 方法来发送消息。

这使您可以直接在 Bun 应用程序中实现实时、事件驱动的通信模式。

subscriber.ts

ts 复制代码
import { RedisClient } from "bun";

const subscriber = new RedisClient("redis://localhost:6379");
await subscriber.connect();

await subscriber.subscribe("my-channel", (message, channel) => {
  console.log(`收到消息: "${message}" 来自频道: "${channel}"`);
  // 收到消息: "Hello from Bun!" 来自频道: "my-channel"
});

publisher.ts

ts 复制代码
import { RedisClient } from "bun";

const publisher = new RedisClient("redis://localhost:6379");
await publisher.connect();

// 短暂延迟以确保订阅者已准备就绪
setTimeout(() => {
  publisher.publish("my-channel", "Hello from Bun!");
}, 100);

并发 bun test

bun test 现在支持使用 test.concurrent 在同一文件内并发运行多个 async 测试。这可以显著加速受 I/O 限制的测试套件,例如那些发起网络请求或与数据库交互的测试。

concurrent.test.ts

ts 复制代码
import { test, expect } from "bun:test";

// 这三个测试将并行运行。
// 总执行时间将约为 1 秒,而不是 3 秒。
test.concurrent("向服务器 1 发送请求", async () => {
  const response = await fetch("https://example.com/server-1");
  expect(response.status).toBe(200);
});

test.concurrent("向服务器 2 发送请求", async () => {
  const response = await fetch("https://example.com/server-2");
  expect(response.status).toBe(200);
});

// 可以与 .each、.only 或其他修饰符链式调用:
test.concurrent.each([
  "https://example.com/server-4",
  "https://example.com/server-5",
  "https://example.com/server-6",
])("向服务器 %s 发送请求", async (url) => {
  const response = await fetch(url);
  expect(response.status).toBe(200);
});

使用 describe.concurrent 并发运行测试组。

describe.concurrent.test.ts

ts 复制代码
import { describe, test, expect } from "bun:test";

describe.concurrent("服务器测试", () => {
  test("向服务器 1 发送请求", async () => {
    const response = await fetch("https://example.com/server-1");
    expect(response.status).toBe(200);
  });
});

test("串行测试", () => {
  expect(1 + 1).toBe(2);
});

默认情况下,最多会同时运行 20 个测试。您可以使用 --max-concurrency 标志来更改此设置。

concurrentTestGlob 使特定文件并发运行

要使特定文件并发运行,您可以在 bunfig.toml 中使用 concurrentTestGlob 选项。

bunfig.toml

toml 复制代码
[test]
concurrentTestGlob = "**/integration/**/*.test.ts"

# 您也可以提供一个模式数组。
# concurrentTestGlob = [
#   "**/integration/**/*.test.ts",
#   "**/*-concurrent.test.ts",
# ]

当使用 concurrentTestGlob 时,所有匹配 glob 的文件中的测试都将并发运行。

test.serial 使特定测试顺序执行

当您使用 describe.concurrent--concurrentconcurrentTestGlob 时,您可能仍然希望某些测试顺序执行。您可以使用新的 test.serial 修饰符来实现这一点。

test.serial.test.ts

ts 复制代码
import { test, expect } from "bun:test";

describe.concurrent("并发测试", () => {
  test("异步测试", async () => {
    await fetch("https://example.com/server-1");
    expect(1 + 1).toBe(2);
  });

  test("异步测试 #2", async () => {
    await fetch("https://example.com/server-2");
    expect(1 + 1).toBe(2);
  });

  test.serial("串行测试", () => {
    expect(1 + 1).toBe(2);
  });
});

使用 --randomize 随机顺序运行测试

并发测试有时会暴露出测试对执行顺序或共享状态的意外依赖。您可以使用 --randomize 标志以随机顺序运行测试,以便更容易发现这些依赖关系。

当您使用 --randomize 时,Bun 将输出该次运行的种子。为了在调试时重现完全相同的测试顺序,您可以使用 --seed 标志并附上打印的值。使用 --seed 会自动启用随机化。

sh 复制代码
# 以随机顺序运行测试
bun test --randomize

# 种子会在测试摘要中打印
# ... 测试输出 ...
#  --seed=12345
# 2 通过
# 8 失败
# 在 2 个文件中运行了 10 个测试。 [50.00ms]

# 使用种子重现相同的运行顺序
bun test --seed 12345

bun test 现在支持链式修饰符

您现在可以在 testdescribe 上链式调用修饰符,如 .failing.skip.only.each。以前,这会导致错误。

ts 复制代码
import { test, expect } from "bun:test";

// 这个测试预期会失败,并且它为数组中的每个项目运行。
test.failing.each([1, 2, 3])("每个 %i", (i) => {
  if (i > 0) {
    throw new Error("此测试预期失败。");
  }
});

CI 环境中更严格的 bun test

为防止意外提交,bun test 现在在 CI 环境中(即 CI=true 时)会在两种新情况下抛出错误:

  • 如果测试文件包含 test.only()
  • 如果快照测试(.toMatchSnapshot().toMatchInlineSnapshot())尝试在没有 --update-snapshots 标志的情况下创建新快照。

这有助于防止临时聚焦的测试或无意的快照更改被合并。要禁用此行为,您可以设置环境变量 CI=false

测试执行顺序改进

测试运行器的执行逻辑已被重写,以提高可靠性和可预测性。这解决了大量关于 describe 块和钩子(beforeAllafterAll 等)执行顺序略有意外的问题。新的行为与 Vitest 等测试运行器更加一致。

并发测试限制

  • 当使用 test.concurrentdescribe.concurrent 时,不支持 expect.assertions()expect.hasAssertions()
  • 不支持 toMatchSnapshot(),但支持 toMatchInlineSnapshot()
  • beforeAllafterAll 钩子不会并发执行。

bun feedback

在下一个 Bun 版本中

bun feedback <文件或文本> 让向 Bun 团队发送关于 Bun 的反馈变得更加容易 pic.twitter.com/CkJAKwllzU

--- Jarred Sumner (@jarredsumner) 2025年9月16日

Node.js 兼容性改进

node:http

http.createServer 现在支持 CONNECT 方法。这允许使用 Bun 创建 HTTP 代理。

node:dns

dns.resolve 的回调现在与 Node.js 匹配,不再传递额外的 hostname 参数。dns.promises.resolve 现在对于 A/AAAA 记录正确返回字符串数组而不是对象。

node:worker_threads

修复了一个在使用 port.on('message', ...)port.addEventListener('message', ...) 时,MessagePort 通信在转移到 Worker 后会失败的 bug。这是由于 Web Workers 和 worker_threads 之间的差异导致的。

node:crypto

修复了在使用 JWK 格式的椭圆曲线密钥且 dsaEncoding: 'ieee-p1363' 时,crypto.createSign().sign() 可能发生的假设性崩溃。

node:http2

修复了关闭套接字时的内存泄漏问题。

node:net

修复了 net.connect() 中的句柄泄漏问题,该问题导致在建立大量连接时内存使用量增长。

node:tty

在 Windows 上,TTY 原始模式 (process.stdin.setRawMode(true)) 现在能正确处理终端 VT 控制序列,提高了与 Node.js 的兼容性,并支持诸如带括号的粘贴模式等功能。

使用 --use-system-ca 来使用系统的受信任证书

Bun 现在可以配置为使用操作系统的受信任根证书来建立 TLS 连接,此外还可以使用内置的 Mozilla CA 存储。这在具有自定义证书颁发机构的企业环境中或用于信任本地安装的自签名证书时非常有用。

要启用此功能,可以使用新的 --use-system-ca 命令行标志,或者设置 NODE_USE_SYSTEM_CA=1 环境变量,这使得 Bun 与 Node.js 的行为保持一致。

sh 复制代码
# 此命令将使用操作系统的受信任 CA 来验证
# "internal.service.corp" 的证书。

bun run --use-system-ca index.js
ts 复制代码
// index.js
const response = await fetch("https://internal.service.corp");
console.log(response.status);

process.report.getReport() 现已在 Windows 上实现

与 Node.js 兼容的 API process.report.getReport() 现已在 Windows 上完全实现。以前,这会抛出"未实现"错误。此函数生成关于当前进程的全面诊断报告,包括系统信息、JavaScript 堆统计信息、堆栈跟踪和已加载的共享库。

此更改提高了依赖此 API 进行诊断或环境检测的工具的兼容性。

ts 复制代码
// 在 Windows 上,现在这会返回一个详细的报告对象。
const report = process.report.getReport();

console.log(report.header.osVersion); // 例如:"Windows 11 Pro"
console.log(report.header.cpus.length > 0); // true
console.log(report.javascript.heap.heapSpaces.length > 0); // true

bun build --compile 中的 Windows 代码签名

bun build --compile 可以从 JavaScript 或 TypeScript 入口点创建独立的可执行文件。在 Windows 上,这些可执行文件通常基于预先签名的 bun.exe 二进制文件。以前,当 Bun 嵌入应用程序代码和资源时,它会使原始签名失效,从而阻止开发人员应用他们自己的代码签名证书。

此版本引入了自动的"Authenticode"签名剥离。当您创建可执行文件时,Bun 现在会移除原始签名,允许您使用标准的 Windows 工具(如 signtool.exe)使用自己的证书对编译后的二进制文件进行签名。这对于向 Windows 用户分发受信任的应用程序至关重要。

sh 复制代码
# 1. 将您的应用程序编译为独立可执行文件
bun build ./index.ts --compile --outfile my-app.exe

# 2. 使用您自己的证书对生成的可执行文件进行签名
signtool.exe sign /f MyCert.pfx /p MyPassword my-app.exe

Bun.build 新增 jsx 配置对象

Bun.build 中 JSX 转换的配置现在集中在一个新的 jsx 对象中。这包括先前通过 tsconfig.json 配置的选项,如 jsxFactoryjsxFragmentjsxImportSource

ts 复制代码
await Bun.build({
  entrypoints: ["./index.jsx"],
  outdir: "./dist",
  jsx: {
    runtime: "automatic", // "automatic" 或 "classic"
    importSource: "preact", // 默认为 "react"
    factory: "h", // 默认为 "React.createElement"
    fragment: "Fragment", // 默认为 "React.Fragment"
    development: false, // 使用 `jsx-dev` 运行时,默认为 `false`
    sideEffects: false,
  },
});

Bun.SQL 中的 sql.array 辅助函数

Bun.SQL 中的 sql.array 辅助函数使得处理 PostgreSQL 数组类型变得容易。您可以将数组插入到数组列中,并指定 PostgreSQL 数据类型以进行适当的类型转换。

ts 复制代码
import { sql } from "bun";

// 插入一个文本值数组
await sql`
  INSERT INTO users (name, roles)
  VALUES (${"Alice"}, ${sql.array(["admin", "user"], "TEXT")})
`;

// 使用 sql 对象表示法更新数组值
await sql`
  UPDATE users
  SET ${sql({
    name: "Bob",
    roles: sql.array(["moderator", "user"], "TEXT"),
  })}
  WHERE id = ${userId}
`;

// 与 JSON/JSONB 数组一起使用
const jsonData = await sql`
  SELECT ${sql.array([{ a: 1 }, { b: 2 }], "JSONB")} as data
`;

// 支持各种 PostgreSQL 类型
await sql`SELECT ${sql.array([1, 2, 3], "INTEGER")} as numbers`;
await sql`SELECT ${sql.array([true, false], "BOOLEAN")} as flags`;
await sql`SELECT ${sql.array([new Date()], "TIMESTAMP")} as dates`;

sql.array 辅助函数支持所有主要的 PostgreSQL 数组类型,包括 TEXTINTEGERBIGINTBOOLEANJSONJSONBTIMESTAMPUUIDINET 等等。

顶层 await 改进

Bun 的打包器现在对顶层 await 有了更好的支持。

当存在使用顶层 await 的循环依赖时,Bun 现在会将模块包装在 await Promise.all 中以确保它们全部加载。我们还修复了一些边缘情况,这些情况可能导致在某些情况下打包的模块中缺少 async 关键字。

相关推荐
晚星star3 小时前
2.2 Node的模块实现
前端·node.js
啃火龙果的兔子6 小时前
可以指定端口启动本地前端的npm包
前端·npm·node.js
葛小白18 小时前
Node.js网页本地部署
node.js
jun_不见9 小时前
nest初体验-用nest实现一个简单的CRUD功能
前端·node.js·全栈
我没想到原来他们都是一堆坏人10 小时前
常用npm源与nrm
前端·npm·node.js
Asurplus11 小时前
Centos7安装Node.js环境
centos·node.js·nvm·nodesource
李广山Samuel1 天前
四、Node-OPCUA 进阶(2)-OPCUA服务器(一)
node.js
妮妮喔妮1 天前
Webpack和Vite优化的区别
前端·webpack·node.js
谢尔登1 天前
a 标签的跳转机制
前端·javascript·webpack·node.js
小皮虾1 天前
搞全栈还在纠结 POST、GET、RESTful?试试这个,像调用本地函数一样写接口
前端·node.js·全栈