Bun v1.2.19发布,node_modules隔离,sql比node快6倍

大家好,我是农村程序员,独立开发者,行业观察员,前端之虎陈随易。我会在这里分享关于 独立开发编程技术思考感悟 等内容,欢迎关注。

技术群与交朋友请在个人网站联系我,网站 1️⃣:chensuiyi.me,网站 2️⃣:me.yicode.tech

如果你觉得本文有用,一键三连 (点赞评论转发),就是对我最大的支持~


node_modules 隔离

这是本次更新最令人兴奋的功能了,请看上图,左侧是 v1.2.18 的 node_modules 结构,所有依赖都平铺到一起。右侧是 v1.2.19 的 node_modules 结构,在使用 workspaces 管理的 monorepo 多项目仓库可以拥有自己的 node_modules 了。

也就是说,如果你的项目使用了一个依赖的不同版本,那么现在可以多版本共存了。也正是因为这个功能,不少人表示,是时候进入到 Bun 的世界了。

但,这个功能不是默认启用的,使用命令 bun install --linker=isolated 来手动指定安装方式为隔离模式。更好的方法则是在项目根目录创建 bunfig.toml 文件,配置如下:

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

这样直接运行 bun install,无需指定 --linker=isolated 参数。

Bun.sql 速度现在提高了 5 倍

js 复制代码
import { SQL } from 'bun:sql';

const db = new SQL('postgres://user:pass@host:port/db');

// Bun 自动将这些查询流水线化,
// 将它们发送到服务器,而无需等待每个查询的单独响应。
const queries = [];
for (let i = 0; i < 100; i++) {
    // .execute() 用于即发即弃查询
    queries.push(db`SELECT ${i}`.execute());
}

// 等待所有结果
const results = await Promise.all(queries);
console.log(results.length); // 100

await db.end();

Bun 的内置 PostgreSQL 客户端 Bun.sql 现在可以自动对查询进行流水线化,从而显著提升性能。

流水线化允许将多个查询发送到服务器,而无需等待前一个查询的响应,从而减少网络延迟的影响。

这在并行执行许多小型独立查询时尤其有效 (例如,一个 API 服务器同时处理多个并发请求)。

此更改默认启用,无需更改代码,在基准测试中,对于高并发工作负载,Bun.sql 现在比在 Bun 中运行的 postgres 包快约 3.4倍,比在 Node.js 中运行的 postgres 快约 6倍

手动选择依赖更新

更新依赖的时候,执行 bun update --interactive,简写 bun update -i,即可列出所有待更新依赖,可以手动选择更新哪些依赖。

这个功能跟 npm-check-updates 和 antfu 的 taze 类似,不过能被 bun 内置,还是更方便的。

新增 bun pm pkg 命令

bash 复制代码
# 获取单个属性
bun pm pkg get name
# 获取多个属性
bun pm pkg get name version
# 设置单个属性
bun pm pkg set name="my-package"
# 设置多个属性
bun pm pkg set scripts.test="jest" version=2.0.0
# 删除单个属性
bun pm pkg delete description
# 删除嵌套属性
bun pm pkg delete scripts.test contributors[0]

虽然好像大部分时候用不上,但也非常实用,有时候需要快速修改属性,就不需要创建单独的脚本文件,或者引入第三方包了。

在 workspaces 中更快的安装速度

修复了导致安装过程中多次重新评估工作区包的错误,这使得使用 Bun 工作区的 Monorepos 的安装速度更快、更可靠。

json 复制代码
{
    "dependencies": {
        "react": "18.2.0"
    },
    "devDependencies": {
        "react": "18.3.0"
    },
    "peerDependencies": {
        "react": "18.2.1"
    }
}

当多个依赖组都有相同的依赖时,目前已调整解析顺序为 devDependencies > optionalDependencies > dependencies > peerDependencies

bun pm pack 增加 --quiet 参数

bun pm pack 命令现在支持 --quiet 参数,使用 --quiet 标志后,它会抑制所有详细输出,仅将生成的 tarball 文件名打印到 stdout。

这对于需要捕获文件名的脚本和自动化工作流程尤其有用。

bash 复制代码
$ bun pm pack
bun pack v1.2.18

packed 131B package.json
packed 40B index.js

my-package-1.0.0.tgz

Total files: 2
Shasum: f2451d6eb1e818f500a791d9aace80b394258a90
Unpacked size: 171B
Packed size: 249B

👆 这是不加 --quiet 参数的输出。

bash 复制代码
$ bun pm pack --quiet
> my-package-1.0.0.tgz

👆 这是加上 --quiet 参数的输出。

bash 复制代码
TARBALL=$(bun pm pack --quiet)
echo "Created: $TARBALL"
> Created: my-package-1.0.0.tgz

👆 具体应用场景和效果如上。

bun installbun add 现在会读取并应用项目 .npmrc 文件中的 link-workspace-packagessave-exact 设置,这允许更精细地控制依赖项管理,与其他包管理器保持一致。

bash 复制代码
# ./.npmrc
save-exact=true

例如,要始终保存精确版本而不是使用 ^ 前缀,可以设置 save-exact=true

json 复制代码
{
    "dependencies": {
        "is-odd": "3.0.1"
    }
}

执行 bun add is-odd 命令,就会在 package.json 中安装固定版本的依赖。

新增 bun why 命令

为了帮助我们调试 node_modules 目录,Bun 现在添加了 bun why <package> 命令,它会跟踪软件包安装的依赖关系链,准确展示该软件包为何成为项目的一部分。

该命令支持使用 glob 模式同时查询多个包,例如 bun why "@types/*" 以及 --depth--top 等标志来控制输出的详细程度。

bash 复制代码
# See the dependency path to a package
$ bun why react

react@18.2.0
  └─ my-app@1.0.0 (requires ^18.0.0)

# Use glob patterns to query multiple packages
$ bun why "@types/*"

@types/react@18.2.15
  └─ dev my-app@1.0.0 (requires ^18.0.0)

@types/react-dom@18.2.7
  └─ dev my-app@1.0.0 (requires ^18.0.0)

顶级的 catelogcatelogs

json 复制代码
// package.json
{
    "name": "my-monorepo",
    "workspaces": ["packages/*"],
    "catalog": {
        "react": "18.2.0"
    },
    "catalogs": {
        "testing": {
            "@testing-library/react": "16.0.0"
        }
    }
}

为了简化配置,bun install 现在支持在根 package.json 的顶层定义依赖项目录。以前,这些字段必须嵌套在 workspaces 对象中,这可能不太直观。现在,可以在根级别声明它们,以实现更清晰的设置。

bun test 与测试资源管理器集成

官方 Bun VS Code 扩展 现在与本机测试资源管理器 UI 集成,bun test 现在可以与扩展通信以报告测试发现、进度和结果。

人工智能代理的紧凑输出

Claude Code 等人工智能代理中运行时,bun test 输出更加紧凑,节省了上下文窗口。

test.each 中的 $variable 替换

js 复制代码
import { test, expect } from 'bun:test';

const testCases = [
    { user: { name: '张三' }, a: 1, b: 2, expected: 3 },
    { user: { name: '李四' }, a: 5, b: 5, expected: 10 }
];

// 以下测试将会生成如下标题
// ✓ 添加 1 和 2 给 张三
// ✓ 添加 5 和 5 给 李四
test.each(testCases)('添加 $a 和 $b 给 $user.name', ({ a, b, expected }) => {
    expect(a + b).toBe(expected);
});

Bundler 删除未使用的 Symbol.for() 调用

Input:输入:

js 复制代码
Sym bol.for("this will be removed");
const a = Symbol.for("this will be kept");
console.log(a);

Output:输出:

js 复制代码
console.log(Symbol.for('this will be kept'));

Bun 的压缩工具现在能够更智能地消除死代码,使用原始参数 (例如字符串或数字) 对 Symbol.for() 的未使用调用现在已被移除,因为如果生成的符号未被使用,这些调用就不会产生副作用,这可以减小打包文件的大小。

使用 test.coveragePathIgnorePatterns 忽略测试覆盖率报告中的文件

现在,可以使用 bunfig.toml 中新增的 test.coveragePathIgnorePatterns 选项从测试覆盖率报告中排除文件,此功能可用于忽略您不想包含在覆盖率指标中的测试文件、Fixture 或其他非源代码。

该选项接受单个 glob 模式或一个模式数组,路径与这些模式匹配的文件将从生成的覆盖率报告中省略。

toml 复制代码
[test]
# 可以使用单个键值对
# coveragePathIgnorePatterns = "**/__tests__/**"

# 也可以使用数组模式
coveragePathIgnorePatterns = [
  "**/__tests__/**",
  "**/test-fixtures.ts",
]

快照文件标题链接已更新

bash 复制代码
- // Bun Snapshot v1, https://goo.gl/fbAQLP
+ // Bun Snapshot v1, https://bun.sh/docs/test/snapshots

生成的快照文件 (.snap) 中的标头注释包含一个 goo.gl 链接,这是一个即将弃用的网址缩短服务,此链接已更新为 https://bun.sh/docs/test/snapshots,以指向 Bun 的官方快照测试文档。

使用 --sql-preconnect 减少首次查询延迟

bash 复制代码
# 要使用,请设置 DATABASE_URL 并使用标志运行 Bun。
export DATABASE_URL="postgres://user:pass@host:port/db"

# Bun 将在执行 index.js 之前连接到 PostgreSQL。
bun --sql-preconnect index.js

Bun 使用 DATABASE_URL 环境变量来确定连接详细信息,如果预连接尝试失败,应用程序不会崩溃,错误将被妥善处理,并在第一次查询时再次尝试连接。

有了这个功能,就不用费心费力地在代码中处理数据库先连接再使用的问题了,直接从框架层面解决了,牛。

Windows 上经过代码签名的独立可执行文件

bash 复制代码
bun build ./my-script.ts --compile --outfile my-app.exe

在 Windows 上使用 bun build --compile 创建的独立可执行文件现在支持 Authenticode 代码签名。

此前,Bun 会在可执行文件的末尾附加自定义存档格式,此技术与 Windows 验证代码签名的方式不兼容,因为它会在文件签名后对其进行修改。

为了解决这个问题,Bun 现在将捆绑的源代码和资源嵌入到 PE (可移植可执行文件) 文件中的专用 .bun 部分中,这种方法可以保留可执行文件结构的完整性,允许使用 signtool.exe 等工具对其进行签名,而不会使签名失效。

启动速度提高 1mS,内存占用减少 3MB

Bun 现在启动速度大约快了 1 毫秒,并且占用的 RAM 减少了大约 3MB。

这项改进源自对 Zig 代码库的低级优化,Zig 中 LLVM 后端对生命周期注释的支持不完整,这会导致不必要的 memcpy 操作,从而导致额外的内存页面因页面错误而被放入进程的地址空间。

console-depth=N 配置 console.log 深度

js 复制代码
// index.js
const nested = {
    a: {
        b: {
            c: {
                d: 'I am deeply nested'
            }
        }
    }
};
console.log(nested);

// By default, the output is truncated to a depth of 2.
// $ bun run index.js
// { a: { b: [Object] } }

// Run with `--console-depth=4` to see the full object.
// $ bun --console-depth=4 run index.js
// { a: { b: { c: { d: 'I am deeply nested' } } } }

// Alternatively, configure this in `bunfig.toml`:
// [run]
// console.depth = 4

现在可以配置使用 console.log 记录的对象的检查深度,这对于调试深度嵌套的数据结构非常有用,默认深度仍然为 2 (与 Node.js 匹配)。

可以在每次运行时使用 --console-depth CLI 标志进行配置,也可以通过 console.depth 设置在 bunfig.toml 中永久配置:

toml 复制代码
[run]
console.depth = 4

或者使用 --console-depth CLI 标志:

bash 复制代码
bun --console-depth=4 index.js

SIMD 加速多行注释解析

js 复制代码
/*
  This is a very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, very,
  ... and so on for many many many thousands of lines ...
  very, very, very, very, very, very, very, very, very,
  very, very, very, very, very, very, very, very, long
  multiline comment.

  Bun's lexer now uses SIMD instructions to skip over
  this block of text much faster than before.
*/
console.log('This file now parses much faster!');

Bun 的 JavaScript 和 TypeScript 解析器现在使用 SIMD 指令来更快地扫描大型多行注释,这可以显著提高解析超长多行注释的性能。

改进了死 try...catch 块的 tree-shaking

js 复制代码
// input.js
function test() {
    return 'foo';

    // 此代码无法访问,将被删除。
    try {
        return 'bar';
    } catch (e) {
        console.log(e);
    }
}

// bun build --minify ./input.js
// 之前:
function test() {
    return 'foo';
    try {
        return 'bar';
    } catch (e) {
        console.log(e);
    }
}

// 之后:
function test() {
    return 'foo';
}

Bun 的打包器现在可以消除 try...catch...finally 代码块,这些代码块属于死代码路径的一部分,例如 return 语句之后的代码块。

这项改进有助于更有效地移除无法访问的代码,从而减少打包文件的大小。

fs.glob 现在支持模式和 exclude 数组

js 复制代码
import { globSync } from 'node:fs';
import { mkdirSync, writeFileSync } from 'node:fs';

// 创建一些虚拟文件用于演示
mkdirSync('a', { recursive: true });
mkdirSync('b', { recursive: true });
writeFileSync('a/file.js', '');
writeFileSync('a/file.ts', '');
writeFileSync('a/style.css', '');
writeFileSync('b/component.js', '');

// 匹配所有 .js 和 .ts 文件,但排除"b"目录中的任何内容。
const files = globSync(['**/*.js', '**/*.ts'], {
    ignore: ['b/**'] // or `exclude`
});

console.log(files.sort());
// => ["a/file.js", "a/file.ts"]

node:fs 模块的 globglobSyncpromises.glob 函数已得到增强,以便与 node-glob 更加紧密地保持一致。

现在,可以将 glob 模式数组作为第一个参数传递,以便同时匹配多个模式。此外,exclude 选项现在接受 glob 模式数组来过滤结果,为现有的基于函数的过滤提供了强大的替代方案。

@types/bun 变得更智能

js 复制代码
// tsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "target": "esnext",
    "lib": ["esnext"], // "dom" 未包含
    "types": ["bun-types"]
  }
}

// index.ts
import type { EventSource as UndiciEventSource } from "undici";

// 此函数需要全局 EventSource 类型
function logEvents(source: EventSource) {
  source.onmessage = (event) => {
    console.log(event.data);
  };
}

// 现在类型检查正确,因为当 DOM 类型未加载时,Bun 的全局 `EventSource`
// 与 `undici` 的 `EventSource` 兼容。
const es: UndiciEventSource = new EventSource("/my-events");
logEvents(es);

此版本修复了 Bun 内置 TypeScript 类型长期存在的一个问题。此前,浏览器和类 Node.js 环境中都存在的全局类型 (例如 EventSourcePerformanceBroadcastChannel) 都是使用类似 interface EventSource {} 空接口声明的。现在,当在 tsconfig.json 中包含 "lib": ["dom"] 时,它们可以与 DOM 的内置类型合并。

但是,如果不包含 DOM 库,这些空接口将与 Node.js 兼容包中的类型冲突。例如,undici 的 EventSource 将与 Bun 的空全局 EventSource 类型不兼容。

Bun 的类型现在更加智能,它会检测你的 tsconfig.json 中是否存在 "lib": ["dom"],如果不存在,它会自动从 undici-typesnode:perf_hooksnode:worker_threads 等模块扩展相应的 Node.js 兼容类型。这消除了类型错误,并提升了服务器端应用程序的开箱即用体验。

node:module 内置模块中的 SourceMap 类和 findSourceMap() 函数

js 复制代码
import { SourceMap } from 'node:module';

const payload = {
    version: 3,
    file: 'output.js',
    sources: ['input.js'],
    sourcesContent: ['() => {}'],
    names: ['add'],
    mappings: 'AAAA,SAASA,GAAG'
};

const map = new SourceMap(payload);

//payload getter 返回用于构造 SourceMap 的对象
console.log(map.payload);
// { version: 3, file: 'output.js', ... }

// 查找给定生成位置的原始源位置
const entry = map.findEntry(0, 9);
console.log(entry);
// {
//   generatedLine: 0,
//   generatedColumn: 9,
//   originalLine: 0,
//   originalColumn: 9,
//   originalSource: 'input.js',
//   name: 'add'
// }

Bun 现在实现了 node:module 内置模块中的 SourceMap 类和 findSourceMap() 函数。这允许以编程方式解析、检查和搜索源映射,从而提高了与 Node.js 以及依赖此 API 的工具的兼容性。

此实现还修复了解析包含 names 字段的源映射的错误,并解决了源映射解析失败时可能出现的内存泄漏。

现在支持 process.features.typescript

js 复制代码
console.log(process.features.typescript);
// "transform"

console.log(process.features.require_module);
// true

console.log(process.features.openssl_is_boringssl);
// true

为了提高与 Node.js 的兼容性,现已实现 process.features.typescript。此属性返回字符串 transform,反映 Bun 的运行时默认转译 TypeScript。此外,现已支持 process.features.require_moduleprocess.features.openssl_is_boringssl

os.networkInterfaces() 现在可以正确返回 scopeid

js 复制代码
import { networkInterfaces } from 'os';

const interfaces = networkInterfaces();

for (const name of Object.keys(interfaces)) {
    for (const net of interfaces[name]) {
        // 对于 IPv6,`scopeid` 属性现在已正确命名。
        if (net.family === 'IPv6') {
            console.log(net.scopeid);
            // => 0 (or another number)

            console.log(net.scope_id);
            // => undefined
        }
    }
}

os.networkInterfaces() 中有一个错误已被修复,该错误导致 IPv6 网络接口返回 scope_id 属性,为了提高与 Node.js 的兼容性,此属性已重命名为 scopeid。

现已支持 vm.constants.DONT_CONTEXTIFY

js 复制代码
import { createContext, isContext, constants } from 'node:vm';

const sandbox = { a: 1 };

const context = createContext(sandbox, {
    // 使用新常量来防止沙盒对象上下文化
    [constants.DONT_CONTEXTIFY]: true
});

// 原始沙盒对象不是上下文
console.log(`Is sandbox a context? ${isContext(sandbox)}`); // "沙盒是一个上下文吗?false"

// 返回的对象是实际的上下文
console.log(`Is context a context? ${isContext(context)}`); // "上下文是上下文吗?是的"

// 沙盒和上下文不是同一个对象
console.log(`Are they the same? ${sandbox === context}`); // "它们相同吗?false"

node:vm 模块现在支持 vm.constants.DONT_CONTEXTIFY,创建新的 vm.Context 时,可以将此符号作为选项传递,以防止 sandbox 对象被 上下文化

在此模式下,sandbox 对象本身未链接到虚拟机的上下文,并且 vm.isContext(sandbox) 将返回 false。这提高了与 Node.js 的兼容性,并提供了对虚拟机环境更细粒度的控制。此更改还提高了 vm 模块中动态 import() 的可靠性。

v8C++API 改进

js 复制代码
// 使用 V8 C++ API 创建或修改数组的本机插件。
import { processArray } from 'some-native-addon';

// 如果原生插件使用新实现的 V8 API 函数,现在 Bun 中即可正常运行。
const result = processArray([10, 20, 30]);

console.log(result);

Bun 对 V8 C++ API 的实现进行了显著改进,允许原生 Node.js 插件在 Bun 中运行。

此版本新增了对多个用于与数组和对象交互的核心函数的支持,包括 v8::Array::Newv8::Object::Getv8::Object::Setv8::Value::StrictEquals

这使得 Bun 的 API 更接近与 Node.js v24 的完全 ABI 兼容性,这意味着更多的原生模块将可以开箱即用,而无需重新编译。

相关推荐
brzhang2 小时前
颠覆你对代码的认知:当程序和数据只剩下一棵树,能读懂这篇文章的人估计全球也不到 100 个人
前端·后端·架构
躲在云朵里`2 小时前
SpringBoot的介绍和项目搭建
java·spring boot·后端
斟的是酒中桃2 小时前
基于Transformer的智能对话系统:FastAPI后端与Streamlit前端实现
前端·transformer·fastapi
烛阴2 小时前
Fract - Grid
前端·webgl
喵个咪2 小时前
Golang微服框架Kratos与它的小伙伴系列 - 分布式事务框架 - DTM
后端·微服务·go
JiaLin_Denny2 小时前
React 实现人员列表多选、全选与取消全选功能
前端·react.js·人员列表选择·人员选择·人员多选全选·通讯录人员选择
brzhang2 小时前
我见过了太多做智能音箱做成智障音箱的例子了,今天我就来说说如何做意图识别
前端·后端·架构
为什么名字不能重复呢?3 小时前
Day1||Vue指令学习
前端·vue.js·学习
晴空月明3 小时前
结构型模式-架构解耦与扩展实践
后端
eternalless3 小时前
【原创】中后台前端架构思路 - 组件库(1)
前端·react.js·架构