
2023年9月8日,JavaScript社区再次掀起了新一轮热潮:由Jarred Sumner创建的Bun v1.0问世了。然而,随着各种讨论的进行,许多人都在疑惑:Bun的本质是什么?为什么人们会将其与经过时间考验的Node.js相提并论?Bun只是又一个短暂的趋势,还是它将重新定义游戏规则?在本文中,让我们深入了解``Bun,了解其特点,并了解它与深受信任的Node.js相比如何。
什么是Bun?
Bun是一个针对JavaScript和TypeScript应用的超快全能工具包。Bun的美妙之处在于它能够简化开发流程,使其比以往更加顺畅和高效。这是可能的,因为Bun不仅是一个运行时,它还是一个包管理器、一个打包工具和一个测试运行器。想象一下拥有一个JS开发的瑞士军刀;那就是Bun。
Bun解决的问题
Node.js在2009年的诞生是具有突破性的。然而,就像许多技术一样,随着它的发展,它的复杂性也在增加。想象一下城市。随着城市的扩张,交通拥堵可能成为一个问题。
Bun旨在成为缓解这种拥堵的新基础设施,使事物运行更加顺畅和快速。这并不是要重复造轮子,而是要对其进行精益求精,确保我们既获得了速度和简单性,又没有失去JavaScript独特和强大之处的本质。
Bun被设计为Node.js的更快、更精简、更现代化的替代品,因此让我们更详细地比较一下它们的一些差异。但首先让我们讨论另一个话题。
Node.js vs Deno vs Bun
当讨论JavaScript运行时的演变时,很难忽略Deno。Node.js的创始人Ryan Dahl介绍了Deno作为一个新的运行时,旨在解决他在Node.js中发现的一些问题和缺陷。
Deno是一个用于JavaScript和TypeScript的安全运行时。它直接解决了Node.js的许多缺点。例如,Deno原生支持TypeScript,无需外部工具。与Node.js不同,Node.js默认情况下脚本具有广泛的权限,Deno采用安全优先的方法,要求开发人员明确授予权限,以执行可能涉及敏感操作的操作,例如文件系统访问或网络连接。
虽然Deno为Node.js提供了一种引人注目的替代方案,但它还没有达到Node.js的广泛应用。因此,本文重点讨论Bun与深受信任的Node.js之间的对比。
入门
使用Bun,我们可以使用命令bun init -y创建一个空项目。我们生成了一些文件,在index.ts中添加一行,console.log("Hello, Bun!")。在终端中,运行命令bun index.ts以查看“Hello, Bun!”被记录。
Bun vs Node.js:JavaScript运行时
JavaScript运行时是提供所有必要组件以便使用和运行JavaScript程序的环境。
Node.js和Bun都是运行时。Node.js主要是用C++编写的,而Bun是用一种称为Zig的低级通用编程语言编写的。但这只是冰山一角。让我们更仔细地看看在将Bun视为仅运行时时的其他区别。
JavaScript引擎
JavaScript引擎是一个将我们编写的JavaScript代码转换为机器代码的程序,从而使计算机能够执行特定任务。
Node.js使用了谷歌的V8引擎,该引擎驱动Chrome浏览器,而Bun使用了JavaScriptCore(JSC),这是由苹果为Safari开发的开源JavaScript引擎。
V8和JSC具有不同的架构和优化策略。JSC优先考虑更快的启动时间和更少的内存使用,而执行时间略慢一些。另一方面,V8优先考虑快速执行,具有更多的运行时优化,这可能导致更多的内存使用。
这使得Bun启动速度快,比Node.js快4倍。


总结:
bun比deno快2.19倍,比node快4.81倍。
转译器
虽然Node.js是JavaScript的强大运行时,但它不直接支持TypeScript文件。要在Node.js环境中执行TypeScript,需要外部依赖。一个常见的方法是使用构建步骤将TypeScript(TS)转译为JavaScript(JS),然后运行生成的JS代码。以下是一个使用ts-node包的基本设置:
- 1.安装
npm install -D typescript ts-node
- 2.脚本配置
在你的package.json中,你可以设置脚本来简化这个过程:
{
"scripts": {
"start": "ts-node ./path/to/your/file.ts"
}
}
- 3.执行
有了以上脚本,你可以轻松运行你的TypeScript文件:
npm start
相比之下,Bun提供了一种更简化的方法。它内置了一个JavaScript转译器到运行时中。这允许你直接运行.js、.ts、.jsx和.tsx文件。Bun的内置转译器无缝地将这些文件转换为原生JavaScript,无需额外步骤即可立即执行。
bun index.ts
当运行TypeScript文件时,速度差异被放大,因为Node.js需要一个转译步骤才能运行。

Bun花费8ms,Node esbuild花费40ms,tsx花费120ms,而tsx花费350ms。
ESM和CommonJS兼容性
模块系统允许开发人员将代码组织成可重用的段。在JavaScript中,两种主要的模块系统是CommonJS和ES模块(ESM)。CommonJS源自Node.js,使用require和module.exports进行同步模块处理,适用于服务器端操作。
ESM是在ES6中引入的,它采用import和export语句,提供了一种更静态和异步的方法,针对浏览器和现代构建工具进行了优化。让我们使用colors表示CommonJS,使用chalk表示ESM,这两个流行的包用于将彩色输出添加到控制台,以更好地理解模块系统。
Node.js传统上与CommonJS模块系统相关联。下面是Node.js中的典型用法:
// CommonJS in Node.js (index.js)
const colors = require("colors");
console.log(colors.green('Hello, world!'));
对于Node.js中的ES模块,你有两个选择:
你需要在你的package.json中包含"type": "module"。
使用.mjs扩展名。
// ESM in Node.js (index.mjs)
import chalk from 'chalk';
console.log(chalk.blue('Hello, world!'));
从CommonJS过渡到ES模块(ESM)是一个复杂的过程。Node.js在ESM引入后5年才支持它,而无需实验性标志。尽管如此,CommonJS仍然在生态系统中普遍存在。
Bun通过支持两者而不需要任何特殊配置来简化模块系统。Bun的突出特点是其能够在同一文件中支持import和require(),这在Node.js中是无法原生实现的:
// Mixed modules in Bun (index.js)
import chalk from "chalk";
const colors = require("colors");
console.log(chalk.magenta('Hello from chalk!'));
console.log(colors.cyan('Hello from colors!'));
Web APIs
作为基于浏览器的应用程序的重要组成部分,Web API 提供了像 fetch和 WebSocket 这样的工具,用于网络交互。尽管这些已经成为浏览器标准,但它们在诸如 Node.js之类的服务器端环境中的支持一直不一致。
在早期的 Node.js版本中,浏览器中常见的 Web标准 API并没有得到原生支持。开发人员不得不依赖第三方包(例如 node-fetch)来复制这些功能。然而,从Node.js v18开始,对fetch API的实验性支持已经存在,这可能消除了对这些包的需求。
Bun 通过提供对这些Web标准 API的内置支持来简化这一过程。开发人员可以直接使用稳定的fetch、Request、Response、WebSocket 和其他类似于浏览器的 API,而无需使用额外的包。此外,Bun 对这些 Web API 的本地实现确保了它们与第三方替代方案相比更快、更可靠。
以下是兼容 Node.js(v18及以上版本)和Bun的示例。虽然在Node.js中还处于实验阶段,但在Bun中这个功能已经稳定:
// Experiment fetch in Node.js (v18 and above) and built-in fetch in Bun
async function fetchUserData() {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await response.json();
console.log(user.name);
}
fetchUserData(); // Leanne Graham
Hot reloading
Hot reloading是一项功能,通过在代码更改时自动刷新或重新加载应用程序的部分内容,而无需完全重新启动,从而提高开发人员的生产力。
在Node.js生态系统中,您有几种选项来实现Hot reloading。其中一个流行的工具是 nodemon,它会强制重新启动整个进程:
nodemon index.js
另外,从 Node.js v18 开始,引入了一个实验性的 --watch 标志:
node --watch index.js
这两种方法都旨在在代码更改时提供应用程序的热更新。然而,它们可能在某些环境或场景中有不同的行为。
例如,nodemon 可能会导致一些干扰,比如中断 HTTP 和WebSocket连接,而实验性的--watch 标志可能不会提供完整的功能集,并且在 GitHub上报告了一些问题。
Bun 推进了热更新一步。通过使用--hot 标志运行 Bun,即可启用热重新加载:
bun --hot index.ts
与可能需要完全进程重新启动的 Node.js方法不同,Bun 在不终止旧进程的情况下就地重新加载您的代码。这确保了 HTTP 和 WebSocket 连接保持不中断,并且应用程序状态得以保留,从而提供了更流畅的开发体验。
Node.js 兼容性
在过渡到新的运行时或环境时,兼容性通常是开发人员的主要关注点。Bun 通过将自己定位为 Node.js的替代品来解决了这个问题。这意味着现有的 Node.js应用程序和 npm 包可以无缝地集成到 Bun中,而无需进行任何修改。确保此兼容性的关键功能包括:
- 支持内置的
Node.js模块,例如fs、path和net。 - 识别全局变量,如
__dirname和process。 - 遵循
Node.js模块解析算法,包括熟悉的node_modules结构。
Bun仍在不断发展。它旨在增强开发工作流程,是服务器无服务功能等资源有限环境的理想选择。Bun团队正在努力实现全面的Node.js兼容性,并更好地与普遍存在的框架集成。
虽然 Bun 确保与 Node.js的兼容性,但它并不止步于此。Bun 附带了高度优化的标准库API,满足开发人员最需要的功能需求。
Bun APIs
- Bun.file()
延迟加载文件并以各种格式访问它们的内容。与其Node.js对应方法相比,这个方法速度提高了多达 10 倍。
// Bun(index.ts)
const file = Bun.file("package.json");
await file.text();
// Node.js(index.mjs)
const fs = require("fs/promises");
const fileContents = await fs.readFile("package.json", "utf-8");
- Bun.write()
将数据从字符串到Blob写入磁盘的多功能API。它的写入速度比Node.js快多达 3 倍。
// Bun(index.ts)
await Bun.write("index.html", "<html/>");
// Node.js(index.mjs)
const fs = require("fs/promises");
await fs.writeFile("index.html", "<html/>");
- Bun.serve()
使用Web标准API设置HTTP服务器或WebSocket服务器。它每秒能够提供比Node.js多达 4 倍的请求数,并处理比Node.js中的ws包多达 5 倍的WebSocket消息。这种后端能力类似于开发人员在Node.js中使用Express,但具有Bun性能优化的额外好处。
// Bun(index.ts)
Bun.serve({
port: 3000,
fetch(request) {
return new Response("Hello from Bun!");
},
});
// Node.js(index.mjs)
import http from "http";
const server = http
.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello from Node.js!");
});
server.listen(3000);
Bun 还支持 sqlite 和密码内置功能。
Bun 与 Node.js 对比:包管理器
Bun不仅仅是一个运行时;它还是一个包含强大包管理器的高级工具包。如果您在依赖项安装过程中发现自己需要耐心等待,Bun 提供了一个令人耳目一新的更快速的替代方案。即使您不将Bun 用作运行时,其内置的包管理器也可以加快开发工作流程。
查看下表,比较了Bun命令与npm、Node的包管理器:
| Bun | npm | Purpose |
|---|---|---|
| bun install | npm install | 从 package.json 安装所有依赖项 |
| bun add <package> | npm install <package> | 向项目添加新包 |
| bun add <package> --dev | npm install <package> --dev | 添加一个仅用于开发的新包 |
| bun remove <package> | npm uninstall <package> | 从项目中删除一个包 |
| bun update <package> | npm update <package> | 更新特定包到其最新版本 |
| bun run <script> | npm run <script> | 从 package.json 执行指定的脚本 |
乍一看,Bun的命令可能看起来很熟悉,但实际体验却并不平凡。Bun 的安装速度比 npm快上数个数量级。它通过利用全局模块缓存来实现这一点,消除了从npm注册表中下载的冗余文件。此外,Bun使用每个操作系统可用的最快的系统调用,确保了最佳的性能。
以下是从缓存安装 Remix初始项目的依赖项时,Bun 和 npm 的速度比较:

bun CLI 包含一个与 Node.js兼容的包管理器,旨在成为 npm、yarn 和 pnpm的显著更快的替代品
此外,bun run <command> 仅需 7 毫秒,而npm run <command> 需要 176 毫秒。虽然 Node.js 的 npm多年来一直是 JavaScript 包管理的标准,但 Bun真的是一个速度强大的工具,并提供了一个引人注目的替代方案。
Bun 与 Node.js 对比:打包工具
打包是将多个 JavaScript文件合并成一个或多个优化的捆绑包的过程。此过程还可能涉及转换,例如将TypeScript转换为 JavaScript或将代码缩小以减小其大小。
在Node.js 生态系统中,打包通常由第三方工具处理,而不是 Node.js本身。在 Node.js 世界中,一些最流行的打包工具包括 Webpack、Rollup和 Parcel,它们提供了代码分割、树摇和热模块替换等功能。
另一方面,Bun 不仅是一个运行时和一个包管理器,还是一个自己的打包工具。它设计用于为各种平台(包括浏览器中的前端应用程序,如React或 Next.js 应用程序以及 Node.js)打包 JavaScript和 TypeScript代码。
要使用 Bun 进行打包,可以使用简单的命令:
bun build ./index.ts --outdir ./build
此命令将 index.ts文件打包,并将结果输出到./build 目录。Bun的打包过程非常快,比 esbuild快了 1.75 倍,并且明显超过了Parcel和 Webpack等其他打包工具。

Bun耗时0.17秒,esbuild耗时0.3秒,rspack耗时4.45秒,Parcel 2耗时26.32秒,Rollup耗时32秒,Webpack 5耗时38.02秒
Bun的一个突出特点是引入了 JavaScript 宏。这些允许在打包过程中执行 JavaScript函数,并将结果直接内联到最终捆绑包中。此机制为打包提供了一种新的视角。
请看以下示例,在此示例中,Bun 的 JavaScript宏被利用来在打包过程中获取用户名。它不是在运行时执行 API调用,而是在捆绑时间获取数据,将结果直接内联到最终输出中:
// users.ts
export async function getUsername() {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await response.json();
return user.name;
}
// index.ts
import { getUsername } from "./users.ts" with { type: "macro" };
const username = await getUsername();
// build/index.js
var user = await "Leanne Graham";
console.log(user);
虽然Node.js拥有其成熟的打包工具,但Bun提供了一个集成、更快速和创新的替代方案,可能会改变打包格局。
Bun 与 Node.js 对比:测试运行器
测试是软件开发的关键方面,它确保代码的行为符合预期,并在它们进入生产环境之前捕获潜在问题。除了是运行时、
包管理器和打包工具之外,Bun 还是一个测试运行器。
尽管 Node.js开发人员传统上依赖于Jest进行测试,但 Bun 引入了一个内置的测试运行器,该运行器承诺速度快、兼容性好,并具有一系列功能,可满足现代开发工作流程的需求。
Bun 的测试运行器 bun:test设计为与Jest完全兼容,Jest是一种以“expect”风格 API而闻名的测试框架。这种兼容性确保了熟悉 Jest 的开发人员可以轻松过渡到Bun,而无需陡峭的学习曲线。
import { test, expect } from "bun:test";
test("2 + 2", () => {
expect(2 + 2).toBe(4);
});
使用bun test命令执行测试非常简单。此外,Bun 的运行时直接支持 TypeScript 和 JSX,无需额外的配置或插件。
从 Jest 或 Vitest 迁移
Bun 对Jest的全局导入的兼容性表现出其对兼容性的承诺。例如,从 @jest/globals 或 vitest导入将在内部重新映射为 bun:test。这意味着现有的测试套件可以在 Bun 上运行,而无需进行任何代码修改。
// index.test.ts
import { test } from "@jest/globals";
describe("test suite", () => {
test("addition", () => {
expect(1 + 1).toBe(2);
});
});
性能基准测试
Bun的测试运行器不仅关乎兼容性,还关乎速度。在与Zod的测试套件进行基准测试时,Bun 的速度比 Jest 快 13 倍,比 Vitest快 8 倍。Bun 的匹配器也是用快速的本机代码实现的。例如,在Bun中,expect().toEqual()的速度比Jest 快了惊人的 100 倍,比 Vitest 快了 10 倍。
无论您是要迁移现有测试还是启动新项目,Bun都提供了一个符合现代开发需求的强大测试环境。
结论
Node.js长期以来一直是JavaScript世界的基石,它设定基准并指导开发人员。然而,Bun正在成为一个引人注目的挑战者,打破了界限。
虽然Bun还处于早期阶段,但它所产生的热度是不可否认的。目前,它针对 MacOS 和Linux 进行了优化,虽然 Windows支持正在进行中,但某些功能仍在路上。鉴于它所提供的一切,Bun绝对是一个您应该考虑探索的工具包。
浙公网安备 33010602011771号