JavaScript 运行时 Bun 快速预览
今天在 Github 上闲逛,发现了一个集构建、转译、依赖管理于一身的高性能 JavaScript 运行时 Bun,它在 serve、sqlite、ffi 三个方面与 Node.js 和 Deno 的性能对比如下:
本文不对上述对比做任何分析与解释,只是带领大家快速浏览下这个新玩具,以便诸君在茶前饭后之余多一个谈资。
基本特征
更好的性能表现
通过 Node.js 和 Deno 的性能对比(见上文测试截图)可知,Bun 的性能表现非常给力,究其原因,官方给出了以下解释:
- 不同于 Node.js 和 Deno,Bun 使用了 JavaScriptCore 引擎,其启动和执行速度比 V8 引擎要快一些;
- 使用了新兴的系统编程语言 Zig,主要通过手动内存管理对内存进行更细粒度的控制、无隐藏的控制流来提升程序的性能;
- 在前两点的前提下,重新实现了诸如 JSX/TypeScript 转编器、SQLite 客户端、HTTP 客户端、WebSocket 客户端等类库。
更完善的工具链
Bun 提供了更加完善、易用的工具链,比如:
-
内置 JavaScript、TypeScript、JSX 等各种转译器;
Input Loader Output .js JSX + JavaScript .js .jsx JSX + JavaScript .js .ts TypeScript + JavaScript .js .tsx TypeScript + JSX + JavaScript .js .mjs JavaScript .js .cjs JavaScript .js .mts TypeScript .js .cts TypeScript .js .toml TOML .js .css CSS .css .env Env N/A .* file string -
内置构建、依赖管理、任务管理等简单易用的工具。
良好的兼容性
Bun 对 Node.js 生态实现了良好兼容:
- 内置了 fetch、WebSocket、ReadableStream 等 Web API;
- 实现了 Node.js 核心模块(比如 fs、path、Buffer 等)及全局变量(比如 process);
- 实现了 Node.js 的模块解析算法,以便我们可以在 Bun 中使用 npm 包;
- 支持 ESM 和 CommonJS(Bun 内部默认使用 ESM)。
总而言之,Bun 的设计目标是在浏览器之外运行 JavaScript,为我们的基础设施带来了性能等各方面的提升,并通过更好、更简单的工具来提高开发人员的生产力。
快速开始
了解了 Bun 的基本特性后,本节我们通过构建一个简单 Http 服务来感受 Bun 的魅力:
-
执行下面的命令安装 Bun:
curl https://bun.sh/install | bash
-
创建
http.js
,并输入以下内容:export default { port: 3000, fetch(request) { return new Response('Welcome to Bun!'); }, };
-
执行以下命令:
http://localhost:3000
-
最后在浏览器中打开 http://localhost:3000。
配置文件
Bun 的配置文件为 bunfig.toml
,常用配置如下所示:
framework = "next"
logLevel = "debug"
publicDir = "public"
external = ["jquery"]
[macros]
react-relay = { "graphql" = "bun-macro-relay" }
[dev]
port = 5000
[define]
"process.env.bagel" = "'lox'"
[loaders]
".bagel" = "js"
[debug]
editor = "code"
相关属性解释如下:
-
framework
:指定默认使用的framework
版本,bun 将根据bun-framework-${framework}
格式找寻找npm
包; -
logLevel
:指定log
级别(可用值error
、warn
、info
和debug
); -
publicDir
:指定public
目录; -
external
:指定外部扩展,作用等同于 Webpack 的 externals; -
macros
:宏定义,用于替换import
路径,比如:import { graphql } from 'react-relay'; // 将被转换为 import { graphql } from "macro:bun-macro-relay/bun-macro-relay.tsx";
-
dev.port
:指定服务的监听端口(默认3000
); -
define
:作用等同于 Webpack 的 DefinePlugin; -
loaders
:指定各类文件的解析器; -
debug.editor
:当浏览器或命令行抛出异常时,点击出问题的代码时用什么编辑器打开文件,如不指定,将按照以下顺序处理:- 尝试获取环境变量
EDITOR
或VISUAL
的值; - 尝试按照以下顺序打开
Visual Studio Code
(vscode
或code
)、Sublime Text
(subl
或sublime
)、Atom
(atom
)、Neovim
(neovim
或nvim
)、WebStorm
(webstorm
)、IntelliJ
(idea
)、TextMate
(textmate
或textmate
)、Vim
(vim
或vi
)、Emacs
(emacs
)。
- 尝试获取环境变量
在执行命令 bun install
、bun remove
和 bun add
时,将按照以下规则搜索 bunfig.toml
文件:
- 搜索
$XDG_CONFIG_HOME/.bunfig.toml
或$HOME/.bunfig.toml
; - 搜索
./bunfig.toml
。
如果上述路径中的文件均存在,则最终的配置为所有文件配置的并集。
命令行
bun add
添加 npm
包依赖,等同于 yarn add
或 npm install
。
bun install
安装 npm
包依赖,等同于 yarn install
或 npm install
,包的安装速度比 yarn
快 20 倍。
bun run
类似于 npm run
,可直接运行 JavaScript
或 TypeScript
文件及 package.json
中的 scripts
脚本。
根据测试,通过 bun
运行 package.json
中的 scripts
脚本比 npm
运行 package.json
中的 scripts
脚本快 30 倍。
bun create
通过该命令,可快速创建一个模板项目,比如:
创建 Next.js 项目:
bun create next ./app
创建 React 项目:
bun create react ./app
以 Github repo 为模板创建项目:
bun create ahfarmer/calculator ./app
注:bun create
将按照 $BUN_CREATE_DIR/
、$HOME/.bun-create/
、$(pwd)/.bun-create/
、远程地址
的顺序寻找模板。
bun bun
该命令会递归收集指定文件的导入依赖,然后生成包含这些信息的 node_modules.bun
文件,该文件主要包含以下内容:
- 打包后的源代码;
- 打包后的源代码的元数据;
- 项目的元数据和配置信息。
该命令的作用个人以为类似于 Webpack
、Rollup
等工具的打包操作。
bun wiptest
类似于 Jest
的测试执行器,用于内置的 JavaScript
和 TypeScript
项目。
应用
HTTP Server
通过 Bun,可构建出比 Node.js 更快的的 HTTP Server,以下是官方给出的响应为 bun!
的对比测试结果:
Requests per second | Runtime |
---|---|
~64,000 | Node 16 |
~160,000 | Bun |
抛开性能问题不谈,通过 Bun 创建 HTTP Server 也非常简单,比如:
export default {
fetch(req) {
return new Response("HI!");
},
};
// OR
Bun.serve({
fetch(req) {
return new Response("HI!");
},
});
我们也可指定 error
方法来处理异常:
export default {
fetch(req) {
throw new Error('woops!');
},
error(error) {
return new Response('Uh oh!!\n' + error.toString(), { status: 500 });
},
};
// OR
Bun.serve({
fetch(req) {
throw new Error('woops!');
},
error(error: Error) {
return new Response('Uh oh!!\n' + error.toString(), { status: 500 });
},
});
通过 Bun.serve
,可以得到 HTTP Server 的实例,可调用 stop
等方法对 HTTP Server 进行管理。
I/O 操作
Bun
为我们提供了更高效的 I/O 操作接口,其接口示例如下:
// Write 'Hello World' to output.txt
await Bun.write('output.txt', 'Hello World');
// log a file to stdout
await Bun.write(Bun.stdout, Bun.file('input.txt'));
// write the HTTP response body to disk
await Bun.write('index.html', await fetch('http://example.com'));
// OR
await Bun.write(Bun.file('index.html'), await fetch('http://example.com'));
// copy input.txt to output.txt
await Bun.write('output.txt', Bun.file('input.txt'));
SQLite3
Bun
内置了一个高性能的 SQLite3
客户端,其特点如下:
- 简单易用的同步接口;
- 支持事务;
- 支持参数绑定;
- 支持预处理语句;
- 支持自动类型转换(比如
BLOB
自动转换为Uint8Array
); - 能够友好地输出 SQL 语句(通过
toString
方法)。
其接口示例如下:
import { Database } from 'bun:sqlite';
const db = new Database('mydb.sqlite');
db.run(
'CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
);
db.run('INSERT INTO foo (greeting) VALUES (?)', 'Welcome to bun!');
db.run('INSERT INTO foo (greeting) VALUES (?)', 'Hello World!');
// get the first row
db.query('SELECT * FROM foo').get();
// { id: 1, greeting: 'Welcome to bun!' }
// get all rows
db.query('SELECT * FROM foo').all();
// [
// { id: 1, greeting: 'Welcome to bun!' },
// { id: 2, greeting: 'Hello World!' },
// ]
// get all rows matching a condition
db.query('SELECT * FROM foo WHERE greeting = ?').all('Welcome to bun!');
// [
// { id: 1, greeting: 'Welcome to bun!' },
// ]
// get first row matching a named condition
db.query('SELECT * FROM foo WHERE greeting = $greeting').get({
$greeting: 'Welcome to bun!',
});
// [
// { id: 1, greeting: 'Welcome to bun!' },
// ]
总结
本文对 Bun 进行了简单介绍,文中还有许多未涉及的东西需大家自行去探索;本文若有纰漏之处,还望大家能够指正,最后祝大家快乐编码每一天。
参考链接
转载自:https://juejin.cn/post/7119311636178927646