likes
comments
collection
share

JavaScript 运行时 Bun 快速预览

作者站长头像
站长
· 阅读数 18

今天在 Github 上闲逛,发现了一个集构建、转译、依赖管理于一身的高性能 JavaScript 运行时 Bun,它在 serve、sqlite、ffi 三个方面与 Node.js 和 Deno 的性能对比如下:

JavaScript 运行时 Bun 快速预览

JavaScript 运行时 Bun 快速预览

JavaScript 运行时 Bun 快速预览

本文不对上述对比做任何分析与解释,只是带领大家快速浏览下这个新玩具,以便诸君在茶前饭后之余多一个谈资。

基本特征

更好的性能表现

通过 Node.js 和 Deno 的性能对比(见上文测试截图)可知,Bun 的性能表现非常给力,究其原因,官方给出了以下解释:

  • 不同于 Node.js 和 Deno,Bun 使用了 JavaScriptCore 引擎,其启动和执行速度比 V8 引擎要快一些;
  • 使用了新兴的系统编程语言 Zig,主要通过手动内存管理对内存进行更细粒度的控制、无隐藏的控制流来提升程序的性能;
  • 在前两点的前提下,重新实现了诸如 JSX/TypeScript 转编器、SQLite 客户端、HTTP 客户端、WebSocket 客户端等类库。

更完善的工具链

Bun 提供了更加完善、易用的工具链,比如:

  • 内置 JavaScript、TypeScript、JSX 等各种转译器;

    InputLoaderOutput
    .jsJSX + JavaScript.js
    .jsxJSX + JavaScript.js
    .tsTypeScript + JavaScript.js
    .tsxTypeScript + JSX + JavaScript.js
    .mjsJavaScript.js
    .cjsJavaScript.js
    .mtsTypeScript.js
    .ctsTypeScript.js
    .tomlTOML.js
    .cssCSS.css
    .envEnvN/A
    .*filestring
  • 内置构建、依赖管理、任务管理等简单易用的工具。

良好的兼容性

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 级别(可用值 errorwarninfodebug);

  • 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:当浏览器或命令行抛出异常时,点击出问题的代码时用什么编辑器打开文件,如不指定,将按照以下顺序处理:

    • 尝试获取环境变量 EDITORVISUAL 的值;
    • 尝试按照以下顺序打开 Visual Studio Codevscodecode)、Sublime Textsublsublime)、Atomatom)、Neovimneovimnvim)、WebStormwebstorm)、IntelliJidea)、TextMatetextmatetextmate)、Vimvimvi)、Emacsemacs)。

在执行命令 bun installbun removebun add 时,将按照以下规则搜索 bunfig.toml 文件:

  • 搜索 $XDG_CONFIG_HOME/.bunfig.toml$HOME/.bunfig.toml
  • 搜索 ./bunfig.toml

如果上述路径中的文件均存在,则最终的配置为所有文件配置的并集。

命令行

bun add

添加 npm 包依赖,等同于 yarn addnpm install

bun install

安装 npm 包依赖,等同于 yarn installnpm install,包的安装速度比 yarn 快 20 倍。

bun run

类似于 npm run,可直接运行 JavaScriptTypeScript 文件及 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 文件,该文件主要包含以下内容:

  • 打包后的源代码;
  • 打包后的源代码的元数据;
  • 项目的元数据和配置信息。

该命令的作用个人以为类似于 WebpackRollup 等工具的打包操作。

bun wiptest

类似于 Jest 的测试执行器,用于内置的 JavaScriptTypeScript 项目。

应用

HTTP Server

通过 Bun,可构建出比 Node.js 更快的的 HTTP Server,以下是官方给出的响应为 bun! 的对比测试结果:

Requests per secondRuntime
~64,000Node 16
~160,000Bun

抛开性能问题不谈,通过 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 进行了简单介绍,文中还有许多未涉及的东西需大家自行去探索;本文若有纰漏之处,还望大家能够指正,最后祝大家快乐编码每一天。

参考链接