Bun 与 Node.js:你需要知道的一切
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 与 Deno 与 Bun
在讨论 JavaScript 运行时的演变时,很难忽视Deno。Node.js的创建者 Ryan Dahl 介绍了 Deno,作为一种新的运行时,旨在解决他在 Node.js 中发现的一些挑战和遗憾。
Deno 是 JavaScript 和 TypeScript 的安全运行时。它直接解决了 Node.js 的许多缺点。例如,Deno 原生支持 TypeScript,无需外部工具。与 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 与 Node.js:JavaScript 运行时
JavaScript 运行时是一个提供使用和运行 JavaScript 程序所需的所有必要组件的环境。
Node.js 和 Bun 都是运行时。Node.js 主要用 C++ 编写,而 Bun 则使用一种名为Zig 的低级通用编程语言编写。但这仅仅是冰山一角。让我们仔细看看将 Bun 单独视为运行时时的其他差异。
JavaScript 引擎
JavaScript 引擎是一种将我们编写的 JavaScript 代码转换为机器代码的程序,使计算机能够执行特定任务。
Node.js 使用为 Chrome 浏览器提供支持的Google V8 引擎,而 Bun 则使用JavaScriptCore (JSC),这是 Apple 为 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 需要先进行转译步骤才能运行。
ESM 和 CommonJS 兼容性
模块系统允许开发人员将代码组织成可重用的片段。在 JavaScript 中,两个主要的模块系统是CommonJS和ES 模块(ESM)。CommonJS 源自 Node.js,使用require和module.exports进行同步模块处理,非常适合服务器端操作。
ES6 中引入的 ESM 采用import和export语句,提供了一种更静态、更异步的方法,并针对浏览器和现代构建工具进行了优化。让我们使用colorsCommonJS 和chalkESM 这两个流行的包来向控制台添加彩色输出,并更好地理解模块系统。
Node.js 传统上与 CommonJS 模块系统相关联。以下是典型的用法:
// CommonJS in Node.js (index.js)
const colors = require("colors");
console.log(colors.green('Hello, world!'));
对于 Node.js 中的 ES 模块,您有两种选择:
- 您需要将其包含
"type": "module"在您的 中package.json。 - 使用
.mjs扩展。
// ESM in Node.js (index.mjs)
import chalk from 'chalk';
console.log(chalk.blue('Hello, world!'));
从 CommonJS 到 ES 模块(ESM)的过渡是一个复杂的过程。在 ESM 推出后,Node.js 花了五年时间才在非实验性标记的情况下支持它。尽管如此,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 API
Web API 是基于浏览器的应用程序不可或缺的一部分,它提供了类似fetch和 的工具WebSocket来进行 Web 交互。虽然这些 API 已经成为浏览器标准,但它们在 Node.js 等服务器端环境中的支持并不一致。
在早期版本的 Node.js 中,浏览器中常用的 Web 标准 API 并未原生支持。开发者不得不依赖第三方软件包来node-fetch复制此功能。然而,从 Node.js v18 开始,该fetchAPI 已获得实验性支持,这意味着开发者可能不再需要这些软件包。
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
热重载
热重载是一种提高开发人员工作效率的功能,它可以在代码更改时自动实时刷新或重新加载应用程序的各个部分,而无需完全重启。
在 Node.js 生态系统中,有几种实现热重载的选项。一种流行的工具是nodemon,它可以硬重启整个进程:
nodemon index.js
或者,从 Node.js v18 开始,引入了一个实验性--watch标志:
node --watch index.js
这两种方法都旨在在代码更改时提供应用程序的实时重新加载。但是,它们的行为可能有所不同,尤其是在某些环境或场景下。
例如,nodemon可能会导致断开 HTTP 和 WebSocket 连接等中断,而该--watch标志处于实验阶段,可能无法提供全套功能,并且在GitHub 问题中报告了一些问题。
Bun 将热重载功能更进一步。通过在 Bun 中加上--hot以下标志,即可启用热重载:
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 API
Bun.文件()
延迟加载文件并以各种格式访问其内容。此方法比 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()
一个多功能 API,用于将数据写入磁盘,从字符串到 Blob。其写入速度比 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/>");
包子.serve()
使用 Web 标准 API 设置 HTTP 服务器或 WebSocket 服务器。它每秒处理的请求数比 Node.js 多 4 倍,WebSocket 消息处理能力比wsNode.js 中的软件包多 5 倍。这种后端功能让人联想到开发者在 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 命令与Node 的包管理器npm:
乍一看,Bun 的命令可能似曾相识,但实际使用体验却截然不同。Bun 的安装速度比 npm 快几个数量级。它通过利用全局模块缓存来实现这一点,从而消除了从 npm 注册表中进行的冗余下载。此外,Bun 还为每个操作系统配备了最快的系统调用,以确保最佳性能。
以下是从缓存中安装 Remix 启动项目依赖项的速度比较,比较了 Bun 和 npm:
CLIbun包含一个与 Node.js 兼容的包管理器,旨在以更快的速度替代npm、yarn和 。pnpm
此外,abun 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 不仅仅是一个运行时和包管理器,它本身也是一个打包器。它旨在为各种平台打包 JavaScript 和 TypeScript 代码,包括浏览器中的前端应用程序(React 或 Next.js 应用程序)和 Node.js。
要与 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 的匹配器进一步凸显了这一速度优势,这些匹配器采用快速的原生代码实现。例如,expect().toEqual()Bun 的速度比 Jest 快 100 倍,比 Vitest 快 10 倍。
无论您是想迁移现有测试还是启动新项目,Bun 都能提供符合现代开发需求的强大测试环境。
结论
Node.js 长期以来一直是 JavaScript 世界的基石,树立了标杆,引领着开发者。然而,Bun 正以一位值得关注的挑战者的身份崭露头角,不断突破界限。
虽然 Bun 尚处于起步阶段,但它所引发的热议毋庸置疑。目前,它已针对 MacOS 和 Linux 进行了优化,Windows 支持也正在推进中,但部分功能仍待开发。Bun 提供的功能如此丰富,绝对值得您考虑探索。
使用组件进行可视化构建
Builder.io是一个可视化编辑器,可以连接到任何网站或应用程序,并允许您拖放组件。
// Dynamically render your components
export function MyPage({ json }) {
return <BuilderComponent content={json} />
}
registerComponents([MyHero, MyProducts])
后端开发教程 - Java、Spring Boot 实战 - msg200.com