--compile 标志,用于从 TypeScript 或 JavaScript 文件生成独立的二进制可执行文件。
- 命令行
- JavaScript
terminal
cli.ts
cli.ts 打包为可直接运行的可执行文件:
terminal
跨平台交叉编译
使用--target 标志,可以将独立可执行文件编译为与运行 bun build 的机器不同的操作系统、架构或 Bun 版本。
构建 Linux x64 版本(大多数服务器):
- 命令行
- JavaScript
terminal
- 命令行
- JavaScript
terminal
- 命令行
- JavaScript
terminal
- 命令行
- JavaScript
terminal
- 命令行
- JavaScript
terminal
- 命令行
- JavaScript
terminal
支持的目标平台
--target 值的各个部分可以按任意顺序出现,只要它们使用 - 分隔即可。
在 x64 上,Bun 提供一个面向 Nehalem(SSE4.2)的二进制文件,并在运行时选择 AVX2/AVX-512 代码路径。为保持向后兼容,仍接受
-baseline 和 -modern 目标后缀,但它们都会解析为同一个二进制文件;你无需根据目标 CPU 选择其中一个。编译时常量
使用--define 标志可将编译时常量注入可执行文件,例如版本号、构建时间戳或配置值:
- 命令行
- JavaScript
terminal
如需了解更多示例和模式,请参阅构建时常量指南。
生产环境部署
编译后的可执行文件降低内存使用并提升 Bun 启动速度。 通常情况下,Bun 会在import 和 require 时读取并转译 JavaScript 和 TypeScript 文件。这正是 Bun 能够“开箱即用”的原因之一,但这并非没有代价:从磁盘读取文件、解析路径、解析代码、转译以及打印源代码都会消耗时间和内存。
编译后的可执行文件将这些开销从运行时转移到了构建时。
部署到生产环境推荐做法:
- 命令行
- JavaScript
terminal
字节码编译
为提升启动速度,启用字节码编译:- 命令行
- JavaScript
terminal
tsc 启动速度提升两倍:
bun build 命令会稍微变慢。字节码编译不会隐藏源代码。
字节码编译在搭配
--compile 时同时支持 cjs 和 esm 格式。各个标志的作用
--minify 参数会减小转译后输出代码的大小。对于大型应用,这可以节省数兆字节的空间。对于较小的应用,它仍可能略微改善启动时间。
--sourcemap 参数会嵌入使用 zstd 压缩的源映射,这样错误和堆栈跟踪就会指向其原始位置,而不是转译后的位置。发生错误时,Bun 会自动解压并解析源映射。
--bytecode 参数会启用字节码编译。每次在 Bun 中运行 JavaScript 代码时,JavaScriptCore(引擎)都会将源代码编译为字节码。--bytecode 会将这部分解析工作从运行时转移到打包时,从而缩短启动时间。
使用 --bytecode 时,JavaScriptCore 的构建时优化过程也会对生成的字节码运行;--no-optimize-bytecode(Bun.build 中的 optimize: { bytecode: false })则会跳过这些过程。
基于配置文件引导的字节码布局
--bytecode-order 是针对 --bytecode 的配置文件引导优化(PGO)。运行一次可执行文件以记录它使用的函数,然后使用该配置文件再次构建。Bun 会将运行期间使用的字节码集中放在文件开头,这样可执行文件读取和解码自身的内容就会更少:启动更快,内存占用也更低。
在一个大型 CLI 应用中,冷启动时间从 1.0 秒降至 0.53 秒,启动时的内存占用减少了 40 MB。
.github/workflows/release.yml
Bun.build:
build.ts
- 代码变更后,配置文件仍然有效。 函数根据语法哈希进行匹配,而非名称或位置,因此上周构建生成的配置文件仍然适用。如果每次构建时都记录配置文件不方便,可以将其提交到版本控制或缓存。自记录后发生变化的函数会放在已记录函数之后。
- 一个配置文件适用于所有
--target。 - 多个配置文件可覆盖多种应用启动方式:
--bytecode-order=interactive.order,oneshot.order。将最常见的方式列在前面。 - 只有布局会发生变化。 程序行为保持不变,构建仍然可复现。配置文件包含的是哈希,而非源代码。
BUN_BYTECODE_ORDER_OUT中的%p会替换为进程 ID。Worker线程也会被记录。bun:jsc中的bytecodeOrderStats()会报告配置文件覆盖(hot)或未覆盖(cold)的已加载函数数量。如果cold的占比不断上升,就该重新记录了。
JIT 策略
只在启动期间运行的代码通常无法抵消 JIT 编译它所花费的时间。--compile-jit-policy <n>(Bun.build 中的 compile: { jitPolicy: n })会将 JavaScriptCore 的层级提升阈值乘以 n,从而让启动代码在解释器中停留更长时间。默认值 1 表示常规 JIT 策略。该倍数会一直生效,直到程序调用 Bun.unsafe.setJITPolicy(1);程序应在进入交互状态后调用此函数:
build.ts
app.ts
嵌入运行时参数
--compile-exec-argv="args" - 嵌入运行时参数,可在运行时通过 process.execArgv 获取:
- 命令行
- JavaScript
terminal
app.ts
通过 BUN_OPTIONS 传入运行时参数
独立可执行文件会读取 BUN_OPTIONS 环境变量,因此你可以无需重新编译即可传入运行时标志:
terminal
自动加载配置
独立可执行文件可以自动从运行目录加载配置文件。默认:- 禁用 加载
tsconfig.json和package.json— 这些通常只在开发时需要,编译时已经用过了 - 启用 加载
.env和bunfig.toml— 这些通常包含运行时配置,部署时可能有所不同
未来版本中,为了更确定的行为,可能会默认禁用
.env 和 bunfig.toml 加载。运行时启用配置加载
如果您的可执行文件需要在运行时读取tsconfig.json 或 package.json,请使用以下标志启用:
terminal
运行时禁用配置加载
要禁用.env 或 bunfig.toml 以实现确定性执行:
- 命令行
- JavaScript
terminal
作为 Bun CLI 使用
此功能自 Bun v1.2.16 起支持
BUN_BE_BUN=1 环境变量,可以将独立可执行文件作为 bun CLI 本身运行。该可执行文件会忽略其打包的入口点,转而提供完整的 bun CLI。
例如,考虑一个由以下脚本编译而成的可执行文件:
terminal
./such-bun 时,会执行该脚本。
terminal
BUN_BE_BUN=1 环境变量后,该可执行文件的行为会与 bun 二进制文件相同:
terminal
全栈可执行文件
此功能自 Bun v1.2.17 起支持
--compile 标志可以创建一个同时包含服务器和客户端代码的独立可执行文件,非常适合全栈应用。当你在服务器代码中导入 HTML 文件时,Bun 会将前端资源(JavaScript、CSS 等)打包并嵌入可执行文件中。
- 命令行
- JavaScript
terminal
- 你的服务器代码
- Bun 运行时
- 所有前端资源(HTML、CSS、JavaScript)
- 服务器用到的所有 npm 包
terminal
Bun.serve 会使用该对象提供预先打包的资源。
有关构建全栈应用的更多信息,请参阅全栈指南。
Worker
使用独立可执行文件中的 Worker,需要将 Worker 入口也加入构建:- 命令行
- JavaScript
terminal
index.ts
new Worker(path) 中静态已知的路径,并自动打包它们。目前,你需要像前面的示例那样,将 Worker 文件列为入口点。
如果你使用相对路径指向未包含在独立可执行文件中的文件,Bun 会相对于进程当前工作目录从磁盘加载该路径;如果文件不存在,则会报错。
SQLite
使用bun:sqlite 导入时,支持 bun build --compile。
默认情况下,Bun 会相对于进程的当前工作目录解析数据库路径。
index.ts
/usr/bin/hello,且用户的终端当前位于 /home/me/Desktop,Bun 会查找 /home/me/Desktop/my.db。
terminal
嵌入静态资源及文件
独立可执行文件可以直接将文件嵌入二进制文件中,因此单个可执行文件就可以携带应用程序所需的图像、JSON 配置、模板或其他资源。机制原理
使用with { type: "file" } 导入属性来嵌入文件:
index.ts
- 读取文件内容
- 将数据嵌入可执行文件
- 将导入替换为内部路径(以
/$bunfs/为前缀)
Bun.file() 或 Node.js 的 fs API 读取嵌入文件。
用 Bun.file() 读取嵌入文件
Bun.file() 是读取嵌入文件的推荐方式:
index.ts
用 Node.js fs 读取嵌入文件
嵌入文件可以与 Node.js 文件系统 API 一起使用:index.ts
实践示例
嵌入 JSON 配置
index.ts
HTTP 服务器中提供静态资源
在Bun.serve() 中使用静态路由,可高效提供静态文件:
server.ts
嵌入模板文件
index.ts
嵌入二进制文件
index.ts
将文本嵌入为字符串
如果要将文件内容作为字符串使用,请使用text 加载器。.txt 文件默认使用此加载器。对于其他扩展名,可以添加 with { type: "text" },或传入 --loader .md:text。
index.ts
Bun.embeddedFiles 不会列出它。需要文件路径时,请使用 with { type: "file" }。
嵌入 SQLite 数据库
要将 SQLite 数据库嵌入编译后的可执行文件,请在导入属性中设置type: "sqlite",并将 embed 属性设置为 "true"。
数据库文件必须已存在于磁盘。然后在代码中导入:
index.ts
terminal
运行
bun build --compile 时,数据库文件必须存在于磁盘上。embed: "true" 属性会告知打包器将数据库内容包含在编译后的可执行文件中。使用 bun run 正常运行时,Bun 会像处理常规 SQLite 导入一样从磁盘加载数据库文件。嵌入 N-API 插件
可将.node 文件嵌入可执行文件:
index.ts
@mapbox/node-pre-gyp 或类似工具,请直接 require .node 文件,否则打包将无法正确完成。
嵌入目录
使用--asset(或 JavaScript API 中的 compile.assets)将文件或目录树嵌入可执行文件,并保留其原始相对路径。嵌入的文件在运行时位于 import.meta.dir 下,并且可以通过 node:fs(existsSync、statSync、readdirSync、readFileSync)和 Bun.file() 访问。
- 命令行
- JavaScript
terminal
index.ts
--asset 可嵌入多个目录(例如,对于 SvelteKit 构建,可以使用 --asset ./client --asset ./prerendered)。Bun 只嵌入常规文件;它会跳过目录树中的符号链接和空子目录。
你也可以通过 with { type: "file" } 导入属性嵌入单个文件;对于使用 file 加载器的文件(图像、字体等)以及 .wasm 和 .node 文件,也可以将它们作为额外的入口点添加。Bun 会根据 --asset-naming 重命名导入的资源(默认值为 [name]-[hash].[ext]):
运行时检测独立模式
使用Bun.isStandaloneExecutable 来检查当前进程是否正在从编译后的二进制文件运行:
index.ts
Bun.embeddedFiles.length > 0 不同,这种检查不会为每个嵌入文件分配 Blob 对象,因此即使二进制文件嵌入了大量资源,也可以安全地在启动时调用。
列出嵌入文件
Bun.embeddedFiles 将所有嵌入文件以 Blob 对象的形式公开:
index.ts
Bun.embeddedFiles 中每个元素是带 name 属性的 Blob:
server.ts
Bun.embeddedFiles 不含源码文件(.ts、.js 等)以保护源码。内容哈希
默认情况下,Bun 会在每个嵌入文件的名称后附加内容哈希,这有助于从 URL 或 CDN 提供文件时进行缓存失效。如要保留原始名称,请配置资源命名:- 命令行
- JavaScript
terminal
代码压缩(Minification)
启用代码压缩以减少可执行文件体积:- 命令行
- JavaScript
terminal
Windows 平台特有标志
在 Windows 上编译独立可执行文件时,平台特定选项可以自定义生成的.exe 文件的元数据:
- 命令行
- JavaScript
terminal
icon- 指定.ico图标文件路径hideConsole- 隐藏后台终端窗口(GUI 应用用)title- 可执行文件属性中的应用标题publisher- 发布者名称version- 版本号字符串description- 描述信息copyright- 版权声明
macOS 代码签名
为独立可执行文件进行代码签名以消除 Gatekeeper 警告,使用codesign 命令:
terminal
entitlements.plist 文件:
entitlements.plist
--entitlements 标志来支持 JIT 权限:
terminal
terminal
代码拆分
独立可执行文件支持代码拆分。结合--compile 和 --splitting 可生成带有运行时动态加载代码拆分块的可执行文件。
- 命令行
- JavaScript
terminal
terminal
使用插件
插件与独立可执行文件协同工作;使用它们在构建期间转换文件:build.ts
cli.ts
不支持的 CLI 参数
--compile 标志不支持以下标志:
--outdir— 请改用outfile。--public-path--target=node--target=browser(不含 HTML 入口点 — 对于使用.html文件的--compile --target=browser,请参阅独立 HTML)--no-bundle- Bun 始终会将所有内容捆绑到可执行文件中。
API 参考
Bun.build() 中的 compile 选项支持三种形式:
类型
outfile,可执行文件将以入口点命名并写入 outdir;如果未指定 outdir,则写入当前工作目录。这与 bun build --compile 的行为一致。
支持的目标平台
Bun.Build.CompileTarget
完整示例
build.ts