# 字节码缓存
Source: https://bun.zhcndoc.com/bundler/bytecode
使用 Bun 打包器中的字节码缓存加速 JavaScript 执行
字节码缓存是一种构建时优化,通过预先将 JavaScript 编译为字节码来缩短启动时间。例如,启用字节码后编译 TypeScript 的 `tsc`,启动时间可提升 **2 倍**。
## 使用方法
### 基本用法(CommonJS)
使用 `--bytecode` 标志启用字节码缓存。不使用 `--format` 时,输出格式默认为 CommonJS:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build ./index.ts --target=bun --bytecode --outdir=./dist
```
构建会写入两个文件:
* `dist/index.js` - 打包后的 JavaScript(CommonJS)
* `dist/index.js.jsc` - 字节码缓存文件
运行时,Bun 会自动检测并使用 `.jsc` 文件:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun ./dist/index.js # 自动使用 index.js.jsc
```
### 生成独立可执行文件时
使用 `--compile` 创建可执行文件时,Bun 会将字节码嵌入二进制文件中。ESM 和 CommonJS 均可使用:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
# ESM(需要 --compile)
bun build ./cli.ts --compile --bytecode --format=esm --outfile=mycli
# CommonJS(使用或不使用 --compile 均可)
bun build ./cli.ts --compile --bytecode --outfile=mycli
```
生成的可执行文件同时包含代码和字节码。
### ESM 字节码
ESM 字节码需要使用 `--compile`,因为 Bun 会将模块元数据(导入/导出信息)嵌入编译后的二进制文件中。借助这些元数据,JavaScript 引擎可以在运行时完全跳过解析。
如果不使用 `--compile`,ESM 字节码仍需要解析源代码以分析模块依赖,这会违背字节码缓存的目的。
### 与其他优化结合
将字节码与压缩和源映射结合使用:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli
```
* `--minify` 在生成字节码前压缩代码体积(代码更少,字节码更小)
* `--sourcemap` 保持错误定位(错误仍指向原始源码)
* `--bytecode` 消除解析开销。
## 性能影响
性能提升与代码体量成正比:
| 应用大小 | 典型启动速度提升 |
| ------------------ | --------- |
| 小型 CLI (\< 100 KB) | 快 1.5-2 倍 |
| 中大型应用 (> 5 MB) | 快 2.5-4 倍 |
体量越大,受益越明显,因为解析的代码更多。
## 何时使用字节码
### 非常适合:
#### CLI 工具
* 经常调用(如 linter、格式化工具、git 钩子)
* 启动时间即全部用户体验
* 用户能明显感受到 90ms 与 45ms 启动时间的差异
* 例子:TypeScript 编译器、Prettier、ESLint
#### 构建工具和任务运行器
* 在开发过程中执行数百次甚至数千次
* 每次节省数毫秒,累计提升明显
* 改善开发者体验
* 例子:构建脚本、测试运行器、代码生成器
#### 独立可执行文件
* 发布给重视性能的用户
* 单文件分发方便
* 文件体积不如启动速度重要
* 例子:通过 npm 或二进制发布的 CLI
### 不适合:
* ❌ **小型脚本**
* ❌ **只运行一次的代码**
* ❌ **开发构建**
* ❌ **受大小限制的环境**
## 限制
### 仅支持 CommonJS
字节码缓存目前仅支持 CommonJS 输出格式。Bun 打包器会自动将大部分 ESM 代码转换为 CommonJS,但 **顶层 await** 是例外:
```js theme={"theme":{"light":"github-light","dark":"dracula"}}
// 会阻止字节码缓存
const data = await fetch("https://api.example.com");
export default data;
```
**原因**:顶层 await 需要异步模块评估,这无法用 CommonJS 表示。模块图异步化,CommonJS 包装函数模型失效。
**解决方案**:将异步初始化放入函数:
```js theme={"theme":{"light":"github-light","dark":"dracula"}}
async function init() {
const data = await fetch("https://api.example.com");
return data;
}
export default init;
```
现在模块导出的是函数,由调用方根据需要使用 `await`。
### 版本兼容性
字节码**不跨 Bun 版本通用**。字节码格式绑定于 JavaScriptCore 内部表示,不同版本会改变。
更新 Bun 后必须重新生成字节码:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
# 更新 Bun 后
bun build --bytecode ./index.ts --outdir=./dist
```
如果字节码与当前 Bun 版本不匹配,Bun 会忽略它,并回退到解析 JavaScript 源码。
**最佳实践**:将字节码生成纳入 CI/CD 构建流程,不要将 `.jsc` 文件提交到 Git。更新 Bun 时重新生成字节码。
### 仍需源码文件
字节码不会替代 JavaScript。你必须同时部署两个文件:
* `.js` 文件(打包后的源代码)
* `.jsc` 文件(字节码缓存)
运行时流程:
1. Bun 加载 `.js` 文件,发现 `@bytecode` 指令,检查 `.jsc` 文件
2. Bun 加载 `.jsc`
3. Bun 验证字节码哈希与源码匹配
4. 验证通过则使用字节码
5. 否则回退为解析源码
### 字节码不是混淆手段
字节码**不会隐藏你的源代码**。它是性能优化,而非安全措施。
## 生产部署
### Docker
在 Dockerfile 中集成字节码生成:
```dockerfile Dockerfile icon="docker" theme={"theme":{"light":"github-light","dark":"dracula"}}
FROM oven/bun:1 AS builder
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun build --bytecode --minify --sourcemap \
--target=bun \
--compile \
./src/server.ts --outfile=./dist/server
FROM oven/bun:1 AS runner
WORKDIR /app
COPY --from=builder /app/dist/server /app/server
CMD ["./server"]
```
字节码文件与架构无关。
### CI/CD
在构建流水线中生成字节码:
```yaml workflow.yml icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
# GitHub Actions
- name: Build with bytecode
run: |
bun install
bun build --bytecode --minify \
--outdir=./dist \
--target=bun \
./src/index.ts
```
## 调试
### 验证字节码是否生效
检查 `.jsc` 文件是否存在:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
ls -lh dist/
```
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
-rw-r--r-- 1 user staff 245K index.js
-rw-r--r-- 1 user staff 1.1M index.js.jsc
```
`.jsc` 文件通常比 `.js` 大 2-8 倍。
要记录是否使用了字节码,请在环境中设置 `BUN_JSC_verboseDiskCache=1`。
缓存命中时,Bun 会记录:
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
[Disk Cache] Cache hit for sourceCode
```
缓存未命中时:
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
[Disk Cache] Cache miss for sourceCode
```
出现多条缓存未命中日志是正常的:Bun 不会对其内置模块中的 JavaScript 进行字节码缓存。
### 常见问题
**字节码被静默忽略**:通常是 Bun 版本更新导致的缓存版本不匹配,重新生成可解决。
**文件太大**:这是预期的。建议:
* 使用 `--minify` 在生成字节码前减小代码体积
* 压缩 `.jsc` 文件以便通过网络传输(gzip/brotli)
* 评估启动速度的提升是否值得文件体积增加
**顶层 await**:不支持,请用异步初始化函数替代。
## 什么是字节码?
运行 JavaScript 时,JavaScript 引擎并不直接执行源代码,而是经过以下步骤:
1. **解析**:读取代码,生成抽象语法树(AST)
2. **字节码编译**:将 AST 编译为字节码,一种更低级但执行更快的中间表示
3. **执行**:引擎通过解释器或 JIT 编译器执行字节码
以上步骤会在**每次**运行代码时执行。一个每天运行 100 次的 CLI 工具会被解析 100 次;一个无服务器函数会在每次冷启动时进行解析。
每次运行你的代码都会执行以上步骤。如果你的 CLI 每天运行 100 次,则代码被解析 100 次。如果是无服务器函数发生频繁冷启动,解析在每次冷启动时都会进行。
字节码缓存将步骤 1 和 2 移到构建时。运行时直接加载预编译的字节码,加快启动。
现代 JavaScript 引擎使用一种称为**惰性解析**的优化方式。它们不会预先解析所有代码,而是仅在函数首次被调用时解析该函数:
```js theme={"theme":{"light":"github-light","dark":"dracula"}}
// 没有字节码缓存时:
function rarely_used() {
// 这段 500 行的函数仅在调用时被解析
}
function main() {
console.log("Starting app");
// rarely_used() 从未调用,故无解析
}
```
这意味着解析开销并不只是启动时的成本——随着应用程序执行不同的代码路径,解析会在整个生命周期内持续发生。使用字节码缓存后,**所有函数都会被预编译**,包括那些引擎原本会延迟解析的函数。
## 字节码格式
### .jsc 文件内部结构
`.jsc` 文件包含一个序列化的字节码结构。
**头部部分**(每次加载验证):
* **缓存版本**:与 JavaScriptCore 框架版本关联的哈希,确保字节码只在对应版本的 Bun 中运行。
* **代码块类型标签**:指示这是程序、模块、eval 还是函数代码块。
**SourceCodeKey**(验证字节码对应源码):
* **源码哈希**:原始 JavaScript 源码的哈希值。Bun 在使用字节码前会验证该哈希是否匹配。
* **源码长度**:源码的确切长度,用于额外验证。
* **编译标志**:编译上下文,例如严格模式、脚本与模块,以及 eval 上下文类型。相同源码使用不同标志编译会生成不同的字节码。
**字节码指令**:
* **指令流**:字节码操作码,即 JavaScript 的编译表示,以可变长度的指令序列存储。
* **元数据表**:每个操作码都有相关元数据,例如性能分析计数器、类型提示和执行次数(即使这些数据尚未填充)。
* **跳转目标**:为控制流(if/else、循环、switch 语句)预先计算的地址。
* **Switch 表**:用于 switch 语句的优化查找表。
**常量与标识符**:
* **常量池**:代码中的所有字面量值——数字、字符串、布尔值、null、undefined。这些值以 JavaScript 值(JSValues)的形式存储,因此运行时无需从源码解析。
* **标识符表**:代码中使用的所有变量名和函数名,以去重后的字符串形式存储。
* **源码表示标记**:指示常量应如何表示的标志(例如整数、双精度浮点数、大整数等)。
**函数元数据**(每个函数对应):
* **寄存器分配**:函数所需寄存器数,如 `thisRegister`、`scopeRegister`、`numVars`、`numCalleeLocals`、`numParameters`。
* **代码特性**:函数特性位掩码,如是否构造函数、箭头函数、使用 `super`、尾调用等,影响执行行为。
* **词法作用域特性**:严格模式及其他词法上下文。
* **解析模式**:函数被解析时的模式(普通、异步、生成器、异步生成器)。
**嵌套结构**:
* **函数声明和表达式**:每个内嵌函数含独立字节码块。文件包含 100 个函数即有 100 个嵌套字节码块。
* **异常处理**:try/catch/finally 边界及处理地址经过预计算。
* **表达式信息**:字节码位置映射回源码位置,方便报错及调试。
### 字节码不包含什么
**字节码不会嵌入你的源码**。相反:
* JavaScript 源码保存在 `.js` 文件中
* 字节码只存储源码的哈希和长度
* 加载时 Bun 验证字节码与当前源码匹配
这就是为什么你需要同时部署 `.js` 和 `.jsc` 文件:没有对应的 `.js` 文件,`.jsc` 文件就毫无用处。
## 权衡:文件大小
Bytecode files are typically 2-8x larger than the source code.
### 为什么字节码更大?
**字节码指令冗长**
一行压缩后的 JS 代码可能对应数十条字节码指令,例如:
```js theme={"theme":{"light":"github-light","dark":"dracula"}}
const sum = arr.reduce((a, b) => a + b, 0);
```
产生的字节码包括:
* 加载 `arr` 变量
* 取 `reduce` 属性
* 创建箭头函数(其本身含字节码)
* 加载初始值 0
* 设定调用参数数量
* 执行调用
* 结果赋值给 `sum`
每步都是独立操作指令附带元信息。
**常量池存储所有字面量**
例如字符串 `"hello"` 出现 100 次,常量池只存储一次,但标识符表和引用增加额外空间。
**每函数元数据**
即使是一行小函数,也有完整的元数据:
* 寄存器分配信息
* 代码特性掩码
* 解析模式
* 异常处理
* 调试用表达式信息
文件中有 1000 个小函数即有 1000 份这类数据。
**性能分析结构**
即使未填充,也有用来记录性能数据的结构:
* 值类型分析槽
* 数组访问分析槽
* 二元算术分析槽
* 一元算术分析槽
都占用空间。
**预计算控制流**
跳转目标、switch 表、异常边界预先计算存储,加快执行也增加大小。
### 减少体积的策略
**压缩**:
字节码通过 gzip/brotli 压缩效果很好(压缩率 60-70%)。重复的结构和元数据能够高效压缩。
**先压缩代码**
先用 `--minify` 可带来:
* 较短的标识符 → 标识符表更小
* 消除死代码 → 减少字节码
* 常量折叠 → 常量池变小
**权衡考虑**
通常你用 2-4 倍更大的文件换 2-4 倍更快的启动。对 CLI 来说极具价值;对长时间运行服务器,几兆字节无所谓。
## 版本与可移植性
### 跨架构可移植:✅
字节码与架构无关。可以:
* macOS ARM64 构建,部署到 Linux x64
* Linux x64 构建,部署到 AWS Lambda ARM64
* Windows x64 构建,部署到 macOS ARM64
字节码是抽象指令,由运行时 JIT 编译针对架构优化。
### 跨版本可移植:❌
字节码不兼容不同 Bun 版本,原因:
**字节码格式变化**:
JavaScriptCore 的字节码格式会随版本变化。新的操作码会被添加,旧的操作码会被移除或修改,元数据结构也会发生变化。
**版本验证**
`.jsc` 文件头部包含缓存版本哈希。加载时:
1. 读取 `.jsc` 中缓存版本
2. 计算当前 JavaScriptCore 版本哈希
3. 不匹配时字节码**静默拒绝**
4. 回退解析 `.js` 源码
**优雅降级**:
这种设计意味着字节码缓存会“开放失败”——如果出现任何问题(版本不匹配、文件损坏、文件缺失),代码仍然可以正常运行。你可能会看到启动速度变慢,但不会看到错误。
## 未链接与已链接字节码
JavaScriptCore 区分“未链接”和“已链接”字节码。这种分离机制使字节码缓存成为可能:
### 未链接字节码(缓存内容)
`.jsc` 文件保存的是**未链接字节码**。包含:
* 编译的字节码指令
* 代码结构信息
* 常量与标识符
* 控制流信息
不包含:
* 指向运行时对象的指针
* JIT 编译的机器码
* 运行时性能分析数据
* 调用链接信息(函数调用关系)
未链接字节码是**不可变且可共享的**,同一代码多次运行共用一份。
### 已链接字节码(运行时)
运行时,Bun 对字节码完成“链接”过程,生成运行时结构,附加:
* **调用链接信息**:优化函数调用路径
* **性能分析数据**:统计指令执行次数、类型流、数组模式等
* **JIT 编译状态**:基线 JIT 或优化 JIT(DFG/FTL)的代码
* **运行时对象**:JavaScript 对象、原型、作用域指针等
这种已链接表示形式会在每次运行代码时重新创建。这种分离机制可以:
1. 缓存成本高昂的解析编译结果(未链接字节码)
2. 收集运行时分析数据
3. 启用基于分析的 JIT 优化
对于生产环境中的 CLI 和无服务器部署,结合使用 `--bytecode --minify --sourcemap` 可以在将错误映射回原始源代码的同时,提供最佳的启动速度。
# CSS
Source: https://bun.zhcndoc.com/bundler/css
Bun 的打包工具内置支持带有现代特性的 CSS
Bun 的打包工具内置支持 CSS,具有以下功能:
* 将现代/未来特性转换为可在所有浏览器上运行的代码(包括添加厂商前缀)
* 压缩
* CSS 模块
* Tailwind(通过原生打包工具插件)
## 转译
默认启用转译和厂商前缀处理,因此你可以使用现代及未来的 CSS 特性,而无需担心浏览器兼容性。
Bun 的 CSS 解析器和打包器直接移植自 LightningCSS,其打包方式受 esbuild 启发。转译器会将现代 CSS 语法转换为向后兼容的等效写法,使其能够在各种浏览器中正常运行。
感谢 LightningCSS 和 esbuild 的作者所做的工作。
## 浏览器兼容性
默认情况下,Bun 的 CSS 打包目标浏览器包括:
* ES2020
* Edge 88+
* Firefox 78+
* Chrome 87+
* Safari 14+
## 语法降级
### 嵌套
借助 CSS 嵌套,你可以直接在父级代码块中编写子级样式,而无需在 CSS 文件中重复父级选择器。
```scss title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 使用嵌套 */
.card {
background: white;
border-radius: 4px;
.title {
font-size: 1.2rem;
font-weight: bold;
}
.content {
padding: 1rem;
}
}
```
Bun 的 CSS 打包工具会自动将此嵌套语法转换成传统的扁平 CSS,以兼容所有浏览器:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 编译输出 */
.card {
background: white;
border-radius: 4px;
}
.card .title {
font-size: 1.2rem;
font-weight: bold;
}
.card .content {
padding: 1rem;
}
```
你还可以在选择器中嵌套媒体查询和其他 at 规则:
```scss title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.responsive-element {
display: block;
@media (min-width: 768px) {
display: flex;
}
}
```
编译后为:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.responsive-element {
display: block;
}
@media (min-width: 768px) {
.responsive-element {
display: flex;
}
}
```
### 颜色混合
`color-mix()` 函数会在所选色彩空间中按给定比例混合两种颜色。使用它可以创建颜色变体,而无需自行计算结果值。
```scss title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
/* 在 RGB 色彩空间中以 30%:70% 混合蓝色和红色 */
background-color: color-mix(in srgb, blue 30%, red);
/* 为悬停状态创建较浅的变体 */
&:hover {
background-color: color-mix(in srgb, blue 30%, red, white 20%);
}
}
```
Bun 的 CSS 打包工具会在构建时评估这些颜色混合(当所有颜色值已确定,非 CSS 变量),生成适用于所有浏览器的静态颜色值:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
/* 精确计算得到的结果色 */
background-color: #b31a1a;
}
.button:hover {
background-color: #c54747;
}
```
### 相对颜色
相对颜色语法可以修改现有颜色的单个组成部分。无需重新计算整个颜色,即可调整亮度、饱和度或单独的颜色通道。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.theme-color {
/* 以基础色为起点,将亮度增加 15% */
--accent: lch(from purple calc(l + 15%) c h);
/* 以我们的品牌蓝色为基色,制作一个去饱和版本 */
--subtle-blue: oklch(from var(--brand-blue) l calc(c * 0.8) h);
}
```
Bun 的 CSS 打包工具会在构建时计算这些相对颜色修改(不使用 CSS 变量时),生成适用于浏览器的静态颜色值:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.theme-color {
--accent: lch(69.32% 58.34 328.37);
--subtle-blue: oklch(60.92% 0.112 240.01);
}
```
可将其用于主题生成、无障碍颜色变体,或根据基础颜色生成颜色阶,而不是为每个值硬编码。
### LAB 颜色
现代 CSS 支持感知均匀的颜色空间 LAB、LCH、OKLAB 和 OKLCH,它们可以表示超出标准 RGB 色域的颜色。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.vibrant-element {
/* 一种超出 sRGB 色域边界的鲜艳红色 */
color: lab(55% 78 35);
/* 使用感知色彩空间的平滑渐变 */
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
}
```
Bun 的 CSS 打包器会将这些颜色格式转换为向后兼容的替代方案,以支持不支持这些格式的浏览器:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.vibrant-element {
/* 退回到最接近的 RGB 近似 */
color: #ff0f52;
/* 支持更宽色域的浏览器使用 P3 备选 */
color: color(display-p3 1 0.12 0.37);
/* 支持该格式的浏览器保持原值 */
color: lab(55% 78 35);
background: linear-gradient(to right, #cd4e15, #3887ab);
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
}
```
### 颜色函数
`color()` 函数可以在传统 RGB 之外的预定义色彩空间中指定颜色,让你能够使用更宽的色域。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.vivid-element {
/* 使用 Display P3 色彩空间实现更宽色域 */
color: color(display-p3 1 0.1 0.3);
/* 使用 A98 RGB 色彩空间 */
background-color: color(a98-rgb 0.44 0.5 0.37);
}
```
对于不支持这些色彩空间的浏览器,Bun 的 CSS 打包器会添加 RGB 备选值:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.vivid-element {
/* 优先使用 RGB 备选以获得最大兼容 */
color: #fa1a4c;
/* 保留原色以支持兼容浏览器 */
color: color(display-p3 1 0.1 0.3);
background-color: #6a805d;
background-color: color(a98-rgb 0.44 0.5 0.37);
}
```
### HWB 颜色
HWB(色相、白度、黑度)颜色模型基于纯色相中混合了多少白色或黑色来表示颜色。与 RGB 或 HSL 值相比,使用这种方式可以更直接地创建浅色和深色变体。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.easy-theming {
/* 纯青色,无白无黑 */
--primary: hwb(180 0% 0%);
/* 同色相,加入 20% 白色(色调) */
--primary-light: hwb(180 20% 0%);
/* 同色相,加入 30% 黑色(阴影) */
--primary-dark: hwb(180 0% 30%);
/* 添加白色和黑色的柔和版本 */
--primary-muted: hwb(180 30% 20%);
}
```
Bun 的 CSS 打包器会将 HWB 颜色转换为 RGB,以兼容所有浏览器:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.easy-theming {
--primary: #00ffff;
--primary-light: #33ffff;
--primary-dark: #00b3b3;
--primary-muted: #339999;
}
```
### 颜色表示法
现代 CSS 支持使用空格分隔的 RGB 和 HSL 值(不带逗号),以及带 alpha 通道的十六进制颜色。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.modern-styling {
/* 空格分隔的 RGB 表示法(无逗号) */
color: rgb(50 100 200);
/* 空格分隔带 alpha 的 RGB */
border-color: rgba(100 50 200 / 75%);
/* 带 alpha 通道的 8 位十六进制 */
background-color: #00aaff80;
/* 简化的 HSL 表示 */
box-shadow: 0 5px 10px hsl(200 50% 30% / 40%);
}
```
Bun 的 CSS 打包器会将这些格式转换为旧版浏览器支持的格式:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.modern-styling {
/* 转为旧版需要的逗号格式 */
color: rgb(50, 100, 200);
/* 透明度通道妥善处理 */
border-color: rgba(100, 50, 200, 0.75);
/* 十六进制带 alpha 转为 rgba */
background-color: rgba(0, 170, 255, 0.5);
box-shadow: 0 5px 10px rgba(38, 115, 153, 0.4);
}
```
### light-dark() 颜色函数
`light-dark()` 函数接受两种颜色,并根据当前颜色方案应用其中一种,使样式无需使用媒体查询即可遵循用户的系统偏好。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
:root {
/* 定义支持的颜色方案 */
color-scheme: light dark;
}
.themed-component {
/* 根据系统偏好自动选择颜色 */
background-color: light-dark(#ffffff, #121212);
color: light-dark(#333333, #eeeeee);
border-color: light-dark(#dddddd, #555555);
}
/* 需要时覆盖系统偏好 */
.light-theme {
color-scheme: light;
}
.dark-theme {
color-scheme: dark;
}
```
对于不支持 `light-dark()` 的浏览器,Bun 的 CSS 打包器会将其转换为带有回退值的 CSS 变量:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
:root {
--buncss-light: initial;
--buncss-dark: ;
color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
:root {
--buncss-light: ;
--buncss-dark: initial;
}
}
.light-theme {
--buncss-light: initial;
--buncss-dark: ;
color-scheme: light;
}
.dark-theme {
--buncss-light: ;
--buncss-dark: initial;
color-scheme: dark;
}
.themed-component {
background-color: var(--buncss-light, #ffffff) var(--buncss-dark, #121212);
color: var(--buncss-light, #333333) var(--buncss-dark, #eeeeee);
border-color: var(--buncss-light, #dddddd) var(--buncss-dark, #555555);
}
```
### 逻辑属性
CSS 逻辑属性根据文档的书写模式和文本方向,而不是屏幕的物理方向来定义布局、间距和尺寸,因此布局可以适应不同的书写系统。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.multilingual-component {
/* 适应书写方向的外边距 */
margin-inline-start: 1rem;
/* 无论文本方向,都合理的内边距 */
padding-block: 1rem 2rem;
/* 顶部起始角的边框半径 */
border-start-start-radius: 4px;
/* 尊重书写模式的尺寸 */
inline-size: 80%;
block-size: auto;
}
```
对于不完全支持逻辑属性的浏览器,Bun 的 CSS 打包器会针对每种文本方向将其编译为物理属性:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 左到右语言 */
.multilingual-component:dir(ltr) {
margin-left: 1rem;
padding-top: 1rem;
padding-bottom: 2rem;
border-top-left-radius: 4px;
width: 80%;
height: auto;
}
/* 右到左语言 */
.multilingual-component:dir(rtl) {
margin-right: 1rem;
padding-top: 1rem;
padding-bottom: 2rem;
border-top-right-radius: 4px;
width: 80%;
height: auto;
}
```
如果不支持 `:dir()` 选择器,Bun 会生成额外的回退方案。
### :dir() 选择器
`:dir()` 伪类根据元素的文本方向(RTL 或 LTR)设置样式,该方向由文档或显式的 direction 属性决定。使用它可以编写无需 JavaScript 的方向感知样式。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 根据文本方向应用不同样式 */
.nav-arrow:dir(ltr) {
transform: rotate(0deg);
}
.nav-arrow:dir(rtl) {
transform: rotate(180deg);
}
/* 基于文本流的位置定位元素 */
.sidebar:dir(ltr) {
border-right: 1px solid #ddd;
}
.sidebar:dir(rtl) {
border-left: 1px solid #ddd;
}
```
对于不支持 `:dir()` 选择器的浏览器,Bun 的 CSS 打包器会将其转换为兼容性更广的 `:lang()` 选择器,并使用适当的语言映射:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 作为回退转换为基于语言的选择器 */
.nav-arrow:lang(en, fr, de, es, it, pt, nl) {
transform: rotate(0deg);
}
.nav-arrow:lang(ar, he, fa, ur) {
transform: rotate(180deg);
}
.sidebar:lang(en, fr, de, es, it, pt, nl) {
border-right: 1px solid #ddd;
}
.sidebar:lang(ar, he, fa, ur) {
border-left: 1px solid #ddd;
}
```
如果不支持向 `:lang()` 传入多个参数,Bun 会生成进一步的回退方案。
### `:lang()` 选择器
`:lang()` 伪类根据元素的语言定位元素。要为相关语言组合规则,可以向单个 `:lang()` 传入多个语言代码。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 针对中日韩语言的排版调整 */
:lang(zh, ja, ko) {
line-height: 1.8;
font-size: 1.05em;
}
/* 不同语言组的引号样式 */
blockquote:lang(fr, it, es, pt) {
font-style: italic;
}
blockquote:lang(de, nl, da, sv) {
font-weight: 500;
}
```
对于不支持在 `:lang()` 选择器中使用多个参数的浏览器,Bun 的 CSS 打包器会将此语法转换为行为相同的 `:is()` 选择器:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 使用 :is() 组合多个语言以提高兼容 */
:is(:lang(zh), :lang(ja), :lang(ko)) {
line-height: 1.8;
font-size: 1.05em;
}
blockquote:is(:lang(fr), :lang(it), :lang(es), :lang(pt)) {
font-style: italic;
}
blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) {
font-weight: 500;
}
```
如果需要,Bun 还可以为 `:is()` 生成额外的回退方案。
### :is() 选择器
`:is()` 伪类函数(之前称为 `:matches()`)接收一个选择器列表,如果列表中的任意选择器匹配,则匹配成功。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 不用分别写这些 */
/*
.article h1,
.article h2,
.article h3 {
margin-top: 1.5em;
}
*/
/* 可以写成 */
.article :is(h1, h2, h3) {
margin-top: 1.5em;
}
/* 复杂示例,多个组合 */
:is(header, main, footer) :is(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
```
对于不支持 `:is()` 的浏览器,Bun 的 CSS 打包工具提供使用厂商前缀的替代写法:
```css theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 使用 -webkit-any 回退 */
.article :-webkit-any(h1, h2, h3) {
margin-top: 1.5em;
}
/* 使用 -moz-any 回退 */
.article :-moz-any(h1, h2, h3) {
margin-top: 1.5em;
}
/* 现代浏览器保留原写法 */
.article :is(h1, h2, h3) {
margin-top: 1.5em;
}
/* 复杂示例的回退 */
:-webkit-any(header, main, footer) :-webkit-any(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
:-moz-any(header, main, footer) :-moz-any(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
:is(header, main, footer) :is(h1, h2, .title) {
font-family: "Heading Font", sans-serif;
}
```
与标准化的 `:is()` 选择器相比,带厂商前缀的版本存在一些限制,尤其是在处理复杂选择器时。Bun 只会在带前缀的版本能够正常工作时使用它们。
### :not() 选择器
`:not()` 伪类会排除匹配某个选择器的元素。现代版本支持多个参数,可以通过一个 `:not()` 排除多种模式。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 选择所有按钮,排除主按钮和次按钮 */
button:not(.primary, .secondary) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
/* 应用样式于所有标题,排除侧边栏或页脚内的 */
h2:not(.sidebar *, footer *) {
margin-top: 2em;
}
```
对于不支持 `:not()` 中多个参数的浏览器,Bun 的 CSS 打包器会将此语法转换为行为相同且兼容性更好的形式:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 转换为使用带 :is() 的 :not() */
button:not(:is(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
h2:not(:is(.sidebar *, footer *)) {
margin-top: 2em;
}
```
如果不支持 `:is()`,Bun 会生成进一步的回退:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 额外回退以确保最大兼容 */
button:not(:-webkit-any(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
button:not(:-moz-any(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
button:not(:is(.primary, .secondary)) {
background-color: #f5f5f5;
border: 1px solid #ddd;
}
```
转换后的选择器保留了原始选择器的优先级和行为。
### 数学函数
CSS 包含标准数学函数(`round()`、`mod()`、`rem()`、`abs()`、`sign()`)、三角函数(`sin()`、`cos()`、`tan()`、`asin()`、`acos()`、`atan()`、`atan2()`)以及指数函数(`pow()`、`sqrt()`、`exp()`、`log()`、`hypot()`)。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.dynamic-sizing {
/* 限定值在最小和最大之间 */
width: clamp(200px, 50%, 800px);
/* 四舍五入到最接近的倍数 */
padding: round(14.8px, 5px);
/* 动画或布局的三角函数 */
transform: rotate(calc(sin(45deg) * 50deg));
/* 组合多个函数的复杂数学 */
--scale-factor: pow(1.25, 3);
font-size: calc(16px * var(--scale-factor));
}
```
当所有值都是已知常量(而非变量)时,Bun 的 CSS 打包器会在构建时计算这些表达式:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.dynamic-sizing {
width: clamp(200px, 50%, 800px);
padding: 15px;
transform: rotate(35.36deg);
--scale-factor: 1.953125;
font-size: calc(16px * var(--scale-factor));
}
```
### 媒体查询范围
媒体查询范围语法使用比较运算符(`<`、`>`、`<=`、`>=`)表示断点,而不是使用更冗长的 `min-` 和 `max-` 前缀。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 比较运算符的现代写法 */
@media (width >= 768px) {
.container {
max-width: 720px;
}
}
/* 使用 <= 和 >= 的包含范围 */
@media (768px <= width <= 1199px) {
.sidebar {
display: flex;
}
}
/* 使用 < 和 > 的排他范围 */
@media (width > 320px) and (width < 768px) {
.mobile-only {
display: block;
}
}
```
Bun 的 CSS 打包器会将范围查询转换为传统的媒体查询语法,以兼容所有浏览器:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 转换为传统 min/max 语法 */
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 768px) and (max-width: 1199px) {
.sidebar {
display: flex;
}
}
@media (min-width: 321px) and (max-width: 767px) {
.mobile-only {
display: block;
}
}
```
### 简写属性
CSS 引入了几种将多个长属性组合在一起的简写属性。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
/* 对齐简写 */
.flex-container {
/* align-items 和 justify-items 的简写 */
place-items: center start;
/* align-content 和 justify-content 的简写 */
place-content: space-between center;
}
.grid-item {
/* align-self 和 justify-self 的简写 */
place-self: end center;
}
/* 双值溢出属性 */
.content-box {
/* 第一个值对应水平,第二个垂直 */
overflow: hidden auto;
}
/* 增强的文本装饰 */
.fancy-link {
/* 结合多条文本装饰属性 */
text-decoration: underline dotted blue 2px;
}
/* 双值 display 语法 */
.component {
/* 外层和内层显示类型 */
display: inline flex;
}
```
对于不支持这些简写属性的浏览器,Bun 会将它们转换为对应的组件长属性:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.flex-container {
/* 展开对齐属性 */
align-items: center;
justify-items: start;
align-content: space-between;
justify-content: center;
}
.grid-item {
align-self: end;
justify-self: center;
}
.content-box {
/* 分别控制溢出方向 */
overflow-x: hidden;
overflow-y: auto;
}
.fancy-link {
/* 单独文本装饰属性 */
text-decoration-line: underline;
text-decoration-style: dotted;
text-decoration-color: blue;
text-decoration-thickness: 2px;
}
.component {
/* 单值 display */
display: inline-flex;
}
```
### 双位置渐变
双位置渐变语法会在两个相邻位置指定相同的颜色,从而创建硬色标:产生锐利的过渡,而不是平滑的渐变。它适用于条纹、色带以及其他多色设计。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.striped-background {
/* 在 30%-40% 制造绿色到红色的硬切换 */
background: linear-gradient(
to right,
yellow 0%,
green 20%,
green 30%,
red 30%,
/* 双点位置制造硬边界 */ red 70%,
blue 70%,
blue 100%
);
}
.progress-bar {
/* 制造明显色块 */
background: linear-gradient(
to right,
#4caf50 0% 25%,
/* 绿色从 0% 到 25% */ #ffc107 25% 50%,
/* 黄色从 25% 到 50% */ #2196f3 50% 75%,
/* 蓝色从 50% 到 75% */ #9c27b0 75% 100% /* 紫色从 75% 到 100% */
);
}
```
对于不支持此语法的浏览器,Bun 的 CSS 打包器会通过复制颜色标记,将其转换为传统格式:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.striped-background {
background: linear-gradient(
to right,
yellow 0%,
green 20%,
green 30%,
red 30%,
/* 分成两段色点 */ red 70%,
blue 70%,
blue 100%
);
}
.progress-bar {
background: linear-gradient(
to right,
#4caf50 0%,
#4caf50 25%,
/* 两段色点绿段 */ #ffc107 25%,
#ffc107 50%,
/* 两段色点黄段 */ #2196f3 50%,
#2196f3 75%,
/* 两段色点蓝段 */ #9c27b0 75%,
#9c27b0 100% /* 两段色点紫段 */
);
}
```
### system-ui 字体
`system-ui` 通用字体系列使用设备的原生 UI 字体。
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.native-interface {
/* 使用系统默认 UI 字体 */
font-family: system-ui;
}
.fallback-aware {
/* 带显式回退的系统 UI 字体 */
font-family: system-ui, sans-serif;
}
```
对于不支持 `system-ui` 的浏览器,Bun 的 CSS 打包器会将其扩展为跨平台字体堆栈:
```css title="styles.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.native-interface {
/* 为所有主流平台扩展 */
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Noto Sans",
Ubuntu,
Cantarell,
"Helvetica Neue";
}
.fallback-aware {
/* 扩展字体堆栈后保留原轮廓回退 */
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Noto Sans",
Ubuntu,
Cantarell,
"Helvetica Neue",
sans-serif;
}
```
扩展后的字体堆栈包含适用于 macOS/iOS、Windows、Android 和 Linux 的系统字体,以及针对旧版浏览器的回退字体。
## CSS 模块
Bun 的打包器还支持 CSS 模块,具有以下功能:
* 无需配置即可检测 CSS 模块文件(`.module.css`)
* 组合(`composes` 属性)
* 将 CSS 模块导入 JSX/TSX
* 针对 CSS 模块无效用法的警告/错误
CSS 模块是一种 CSS 文件(扩展名为 `.module.css`),其中所有类名和动画都限定在该文件的作用域内。这有助于避免类名冲突,因为 CSS 声明默认是全局作用域的。
Bun 的打包器会将局部作用域的类名转换为唯一标识符。
### 快速入门
创建一个以 `.module.css` 结尾的 CSS 文件:
```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
color: red;
}
```
```css title="other-styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
color: blue;
}
```
然后你可以导入此文件,比如在 TSX 文件中:
```tsx title="app.tsx" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import styles from "./styles.module.css";
import otherStyles from "./other-styles.module.css";
export default function App() {
return (
<>
>
);
}
```
导入 CSS 模块会得到一个对象,该对象将每个类名映射到其唯一标识符:
```ts title="app.tsx" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import styles from "./styles.module.css";
import otherStyles from "./other-styles.module.css";
console.log(styles);
console.log(otherStyles);
```
输出结果如下:
```ts title="app.tsx" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
{
button: "button_123";
}
{
button: "button_456";
}
```
每个文件中的类名都是唯一的,因此不会发生冲突。
### 组合
CSS 模块可以将类选择器组合在一起,以便在多个类之间复用样式规则。
例如:
```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
composes: background;
color: red;
}
.background {
background-color: blue;
}
```
这与以下写法相同:
```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
background-color: blue;
color: red;
}
.background {
background-color: blue;
}
```
使用 `composes` 时需要遵循以下两条规则:
**组合规则:**
* `composes` 属性必须写在任何常规 CSS 属性之前
* 只能在带有单个类名的简单选择器上使用 `composes`
```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
#button {
/* 无效!`#button` 不是类选择器 */
composes: background;
}
.button,
.button-secondary {
/* 无效!`.button, .button-secondary` 不是简单选择器 */
composes: background;
}
```
### 从独立 CSS 模块文件组合
你也可以从其他 CSS 模块文件中组合类:
```css title="background.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.background {
background-color: blue;
}
```
```css title="styles.module.css" icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
.button {
composes: background from "./background.module.css";
color: red;
}
```
从独立文件组合类时,请确保它们不包含相同的属性。
CSS 模块规范规定,从独立文件中组合具有冲突属性的类属于未定义行为:输出结果可能有所不同,并且不可靠。
# esbuild
Source: https://bun.zhcndoc.com/bundler/esbuild
从 esbuild 迁移到 Bun 的打包器指南
Bun 的打包器 API 深受 esbuild 启发。本页面对这两个 API 进行了并列比较。
有一些行为有所不同:
**默认进行打包。** 与 esbuild 不同,Bun 默认会进行打包;无需使用 `--bundle` 标志。若要分别转译每个文件,
请使用 `Bun.Transpiler`。
**仅限打包器。** 与 esbuild 不同,Bun 的打包器没有内置开发服务器。将其与 `Bun.serve` 及其他运行时 API
结合使用,即可实现相同的效果。esbuild 的 HTTP 选项不适用。
## 性能
Bun 的打包器在 esbuild 的 three.js 基准测试中比 esbuild 快 1.75 倍。
从零开始打包 10 份 three.js,启用源码映射和压缩
## CLI API
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
# esbuild
esbuild --outdir=out --bundle
# bun
bun build --outdir=out
```
在 Bun 的 CLI 中,像 `--minify` 这样的布尔标志不接受参数。像 `--outdir ` 这样需要一个参数的标志,可以写成 `--outdir out` 或 `--outdir=out`。某些标志(如 `--define`)可以重复使用:`--define foo=bar --define bar=baz`。
| esbuild | bun build | 备注 |
| ---------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--bundle` | 不适用 | Bun 始终进行打包;使用 `--no-bundle` 可禁用打包。 |
| `--define:K=V` | `--define K=V` | 语法略有不同;没有冒号。
`esbuild --define:foo=bar`
`bun build --define foo=bar` |
| `--external:` | `--external ` | 语法略有不同;没有冒号。
`esbuild --external:react`
`bun build --external react` |
| `--format` | `--format` | Bun 支持 `"esm"`、`"cjs"` 和 `"iife"`。esbuild 默认为 `"iife"`。 |
| `--loader:.ext=loader` | `--loader .ext:loader` | Bun 支持的内置加载器集合与 esbuild 不同;请参阅[加载器](/bundler/loaders)。esbuild 的加载器 `dataurl`、`binary`、`base64`、`copy` 和 `empty` 尚未实现。
`--loader` 的语法有所不同。
`esbuild app.ts --bundle --loader:.svg=text`
`bun build app.ts --loader .svg:text` |
| `--minify` | `--minify` | 无差异 |
| `--outdir` | `--outdir` | 无差异 |
| `--outfile` | `--outfile` | 无差异 |
| `--packages` | `--packages` | 无差异 |
| `--platform` | `--target` | 为与 tsconfig 保持一致而重命名为 `--target`。不支持 `neutral`。 |
| `--serve` | 不适用 | 不适用 |
| `--sourcemap` | `--sourcemap` | 无差异 |
| `--splitting` | `--splitting` | 无差异 |
| `--target` | 不适用 | 不支持。Bun 的打包器不会执行语法降级。 |
| `--watch` | `--watch` | 无差异 |
| `--allow-overwrite` | 不适用 | 始终不允许覆盖 |
| `--analyze` | 不适用 | 不支持 |
| `--asset-names` | `--asset-naming` | 为与 JS API 中的命名保持一致而重命名 |
| `--banner` | `--banner` | 仅适用于 JS bundle |
| `--footer` | `--footer` | 仅适用于 JS bundle |
| `--certfile` | 不适用 | 不适用 |
| `--charset=utf8` | 不适用 | 不支持 |
| `--chunk-names` | `--chunk-naming` | 为与 JS API 中的命名保持一致而重命名 |
| `--color` | 不适用 | 始终启用 |
| `--drop` | `--drop` | |
| 不适用 | `--feature` | Bun 特有。通过 `import { feature } from "bun:bundle"` 启用编译时死代码消除的功能标志 |
| `--entry-names` | `--entry-naming` | 为与 JS API 中的命名保持一致而重命名 |
| `--global-name` | 不适用 | 不支持 |
| `--ignore-annotations` | `--ignore-dce-annotations` | |
| `--inject` | 不适用 | 不支持 |
| `--jsx` | `--jsx-runtime ` | 支持 `"automatic"`(使用 JSX 转换)和 `"classic"`(使用 `React.createElement`) |
| `--jsx-dev` | 不适用 | Bun 从 `tsconfig.json` 中读取 `compilerOptions.jsx` 来确定默认值。如果 `compilerOptions.jsx` 为 `"react-jsx"`,或者 `NODE_ENV=production`,Bun 将使用 JSX 转换。否则将使用 `jsxDEV`。打包器不支持 `preserve`。 |
| `--jsx-factory` | `--jsx-factory` | |
| `--jsx-fragment` | `--jsx-fragment` | |
| `--jsx-import-source` | `--jsx-import-source` | |
| `--jsx-side-effects` | 不适用 | 始终假定 JSX 没有副作用 |
| `--keep-names` | `--keep-names` | |
| `--keyfile` | 不适用 | 不适用 |
| `--legal-comments` | 不适用 | 不支持 |
| `--log-level` | 不适用 | 不支持。可以在 `bunfig.toml` 中将其设置为 `logLevel`。 |
| `--log-limit` | 不适用 | 不支持 |
| `--log-override:X=Y` | 不适用 | 不支持 |
| `--main-fields` | 不适用 | 不支持 |
| `--mangle-cache` | 不适用 | 不支持 |
| `--mangle-props` | 不适用 | 不支持 |
| `--mangle-quoted` | 不适用 | 不支持 |
| `--metafile` | `--metafile` | |
| `--minify-whitespace` | `--minify-whitespace` | |
| `--minify-identifiers` | `--minify-identifiers` | |
| `--minify-syntax` | `--minify-syntax` | |
| `--out-extension` | 不适用 | 不支持 |
| `--outbase` | `--root` | |
| `--preserve-symlinks` | 不适用 | 不支持 |
| `--public-path` | `--public-path` | |
| `--pure` | 不适用 | 不支持 |
| `--reserve-props` | 不适用 | 不支持 |
| `--resolve-extensions` | 不适用 | 不支持 |
| `--servedir` | 不适用 | 不适用 |
| `--source-root` | 不适用 | 不支持 |
| `--sourcefile` | 不适用 | 不支持。Bun 不支持标准输入。 |
| `--sourcemap` | `--sourcemap` | 无差异 |
| `--sources-content` | 不适用 | 不支持 |
| `--supported` | 不适用 | 不支持 |
| `--tree-shaking` | 不适用 | 始终为 true |
| `--tsconfig` | `--tsconfig-override` | |
| `--version` | 不适用 | 运行 `bun --version` 查看 Bun 的版本。 |
## JavaScript API
| esbuild.build() | Bun.build() | 备注 |
| ------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `absWorkingDir` | 不适用 | 始终设置为 `process.cwd()` |
| `alias` | 不适用 | 不支持 |
| `allowOverwrite` | 不适用 | 始终为 false |
| `assetNames` | `naming.asset` | 使用与 esbuild 相同的模板语法,但必须显式包含 `[ext]`。
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` |
| `banner` | `banner` | 仅适用于 js bundle |
| `bundle` | 不适用 | 始终为 true。使用 `Bun.Transpiler` 在不打包的情况下进行转译。 |
| `charset` | 不适用 | 不支持 |
| `chunkNames` | `naming.chunk` | 使用与 esbuild 相同的模板语法,但必须显式包含 `[ext]`。
`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` |
| `color` | 不适用 | Bun 将日志返回在构建结果的 `logs` 属性中。 |
| `conditions` | `conditions` | 无差异 |
| `define` | `define` | |
| `drop` | `drop` | |
| `entryNames` | `naming` 或 `naming.entry` | Bun 支持 `naming` 键,该键可以是字符串或对象。使用与 esbuild 相同的模板语法,但必须显式包含 `[ext]`。
`ts
Bun.build({
entrypoints: ["./index.tsx"],
// 当为字符串时,这等价于 entryNames
naming: "[name].[ext]",
// 细粒度的命名选项
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` |
| `entryPoints` | `entrypoints` | 大小写差异 |
| `external` | `external` | 无差异 |
| `footer` | `footer` | 仅适用于 js bundle |
| `format` | `format` | 支持 `"esm"`、`"cjs"` 和 `"iife"`。 |
| `globalName` | 不适用 | 不支持 |
| `ignoreAnnotations` | `ignoreDCEAnnotations` | |
| `inject` | 不适用 | 不支持 |
| `jsx` | `jsx.runtime` | 支持 `"automatic"` 和 `"classic"` |
| `jsxDev` | `jsx.development` | |
| `jsxFactory` | `jsx.factory` | |
| `jsxFragment` | `jsx.fragment` | |
| `jsxImportSource` | `jsx.importSource` | |
| `jsxSideEffects` | `jsx.sideEffects` | |
| `keepNames` | `minify.keepNames` | |
| `legalComments` | 不适用 | 不支持 |
| `loader` | `loader` | Bun 支持的内置 loader 集合与 esbuild 不同;请参阅 [加载器](/bundler/loaders)。esbuild 的 loader `dataurl`、`binary`、`base64`、`copy` 和 `empty` 尚未实现。 |
| `logLevel` | 不适用 | 不支持 |
| `logLimit` | 不适用 | 不支持 |
| `logOverride` | 不适用 | 不支持 |
| `mainFields` | 不适用 | 不支持 |
| `mangleCache` | 不适用 | 不支持 |
| `mangleProps` | 不适用 | 不支持 |
| `mangleQuoted` | 不适用 | 不支持 |
| `metafile` | `metafile` | |
| `minify` | `minify` | 在 Bun 中,`minify` 可以是布尔值或对象。
`ts
await Bun.build({
entrypoints: ['./index.tsx'],
// 启用所有压缩选项
minify: true
// 细粒度选项
minify: {
identifiers: true,
syntax: true,
whitespace: true
}
})
` |
| `minifyIdentifiers` | `minify.identifiers` | 请参阅 `minify` |
| `minifySyntax` | `minify.syntax` | 请参阅 `minify` |
| `minifyWhitespace` | `minify.whitespace` | 请参阅 `minify` |
| `nodePaths` | 不适用 | 不支持 |
| `outExtension` | 不适用 | 不支持 |
| `outbase` | `root` | 名称不同 |
| `outdir` | `outdir` | 无差异 |
| `outfile` | `outfile` | 无差异 |
| `packages` | `packages` | 无差异 |
| `platform` | `target` | 支持 `"bun"`、`"node"` 和 `"browser"`(默认值)。不支持 `"neutral"`。 |
| `plugins` | `plugins` | Bun 的插件 API 是 esbuild 的子集。部分 esbuild 插件无需修改即可与 Bun 配合使用。 |
| `preserveSymlinks` | 不适用 | 不支持 |
| `publicPath` | `publicPath` | 无差异 |
| `pure` | 不适用 | 不支持 |
| `reserveProps` | 不适用 | 不支持 |
| `resolveExtensions` | 不适用 | 不支持 |
| `sourceRoot` | 不适用 | 不支持 |
| `sourcemap` | `sourcemap` | 支持 `"none"`、`"linked"`、`"inline"` 和 `"external"` |
| `sourcesContent` | 不适用 | 不支持 |
| `splitting` | `splitting` | 无差异 |
| `stdin` | 不适用 | 不支持 |
| `supported` | 不适用 | 不支持 |
| `target` | 不适用 | 不支持语法降级 |
| `treeShaking` | `treeShaking` | 默认为 `true` |
| `tsconfig` | `tsconfig` | |
| `write` | 不适用 | 设置了 `outdir`/`outfile` 时设为 true,否则设为 false |
## 插件 API
Bun 的插件 API 旨在兼容 esbuild。Bun 不支持 esbuild 的完整插件 API,但已实现核心功能,并且许多第三方 esbuild 插件无需修改即可与 Bun 一起使用。
从长远来看,我们的目标是实现与 esbuild API 的功能对等。如果某些功能无法正常工作,请提交 issue,帮助我们确定优先级。
Bun 和 esbuild 中的插件均通过构造一个 builder 对象定义。
```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(builder) {
// 定义插件
},
};
```
builder 对象的方法会钩入打包过程的各个部分。Bun 实现了 `onStart`、`onEnd`、`onResolve` 和 `onLoad`;但未实现 esbuild 的 `onDispose` 和 `resolve` 钩子。`initialOptions` 已部分实现:它是只读的,并且仅暴露 esbuild 选项的子集。请改用 `config`(即 Bun 的 `BuildConfig` 格式中的同一配置)。
```ts title="myPlugin.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(builder) {
builder.onStart(() => {
/* 当打包开始时调用 */
});
builder.onResolve(
{
/* onResolve 选项 */
},
args => {
return {
/* onResolve 返回值 */
};
},
);
builder.onLoad(
{
/* onLoad 选项 */
},
args => {
return {
/* onLoad 返回值 */
};
},
);
builder.onEnd(result => {
/* 当打包完成时调用 */
});
},
};
```
### onResolve
* 🟢 `filter`
* 🟢 `namespace`
* 🟢 `path`
* 🟢 `importer`
* 🟢 `namespace`
* 🟢 `resolveDir`
* 🟢 `kind`
* 🔴 `pluginData`
* 🟢 `namespace`
* 🟢 `path`
* 🔴 `errors`
* 🟢 `external`
* 🔴 `pluginData`
* 🔴 `pluginName`
* 🔴 `sideEffects`
* 🔴 `suffix`
* 🔴 `warnings`
* 🔴 `watchDirs`
* 🔴 `watchFiles`
### onLoad
* 🟢 `filter`
* 🟢 `namespace`
* 🟢 `path`
* 🟢 `namespace`
* 🔴 `suffix`
* 🔴 `pluginData`
* 🟢 `contents`
* 🟢 `loader`
* 🔴 `errors`
* 🔴 `pluginData`
* 🔴 `pluginName`
* 🔴 `resolveDir`
* 🔴 `warnings`
* 🔴 `watchDirs`
* 🔴 `watchFiles`
# 独立可执行文件
Source: https://bun.zhcndoc.com/bundler/executables
使用 Bun 从 TypeScript 或 JavaScript 文件生成独立可执行文件
Bun 的打包器支持 `--compile` 标志,用于从 TypeScript 或 JavaScript 文件生成独立的二进制可执行文件。
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build ./cli.ts --compile --outfile mycli
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./cli.ts"],
compile: {
outfile: "./mycli",
},
});
```
```ts cli.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
console.log("Hello world!");
```
这会将 `cli.ts` 打包成一个可以直接运行的可执行文件:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
./mycli
```
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
Hello world!
```
所有导入的文件和包都会被打包进可执行文件中,同时包含一份 Bun 运行时。所有内置的 Bun 和 Node.js API 都被支持。
***
## 跨平台交叉编译
使用 `--target` 标志,可以将独立可执行文件编译为与运行 `bun build` 的机器不同的操作系统、架构或 Bun 版本。
构建 Linux x64 版本(大多数服务器):
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp
# 支持 2013 年以前的 CPU,使用 baseline 版本(nehalem)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp
# 仅支持 2013 年及之后的 CPU,使用 modern 版本(haswell)
# modern 版本速度更快,但 baseline 兼容性更广。
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 标准 Linux x64
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
target: "bun-linux-x64",
outfile: "./myapp",
},
});
// 基线版(2013 年前 CPU)
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
target: "bun-linux-x64-baseline",
outfile: "./myapp",
},
});
// 现代版(2013 年及以后 CPU,更快)
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
target: "bun-linux-x64-modern",
outfile: "./myapp",
},
});
```
构建 Linux ARM64 版本(例如 Graviton 或 Raspberry Pi):
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
# 注意:如果未指定架构,默认架构为 x64。
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
target: "bun-linux-arm64",
outfile: "./myapp",
},
});
```
构建 Windows x64 版本:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp
# 支持 2013 年以前的 CPU,使用 baseline 版本(nehalem)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp
# 仅支持 2013 年及以后 CPU,使用 modern 版本(haswell)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp
# 注意:如果未提供 .exe 扩展名,Bun 会自动为 Windows 可执行文件添加该扩展名
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 标准 Windows x64
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
target: "bun-windows-x64",
outfile: "./myapp", // 自动加上 .exe 后缀
},
});
// 基线版或现代版
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
target: "bun-windows-x64-baseline",
outfile: "./myapp",
},
});
```
为 Windows arm64 构建:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --target=bun-windows-arm64 ./path/to/my/app.ts --outfile myapp
# 注意:如果未提供 .exe 扩展名,Bun 会自动为 Windows 可执行文件添加该扩展名
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
target: "bun-windows-arm64",
outfile: "./myapp", // 自动加上 .exe
},
});
```
构建 macOS arm64 版本:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
target: "bun-darwin-arm64",
outfile: "./myapp",
},
});
```
构建 macOS x64 版本:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
target: "bun-darwin-x64",
outfile: "./myapp",
},
});
```
### 支持的目标平台
`--target` 值的各个部分可以按任意顺序出现,只要它们使用 `-` 分隔即可。
| --target | 操作系统 | 架构 | 现代架构 | 基线架构 | Libc |
| -------------------- | ------- | ----- | ---- | ---- | ----- |
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | - |
| bun-windows-arm64 | Windows | arm64 | ✅ | N/A | - |
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
在 x64 平台上,Bun 使用需要 CPU 支持 AVX2 指令的 SIMD 优化。Bun 的 `-baseline` 版本适用于不支持这些指令的旧款 CPU。Bun 安装程序会检测应使用哪个版本,但进行交叉编译时,你可能不知道目标 CPU 的具体情况。这主要影响 Windows x64 和 Linux x64,在 Darwin x64 上则很少遇到。如果你或你的用户看到
`"Illegal instruction"` 错误,可能需要使用 baseline 版本。
***
## 编译时常量
使用 `--define` 标志可将编译时常量注入可执行文件,例如版本号、构建时间戳或配置值:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./src/cli.ts"],
compile: {
outfile: "./mycli",
},
define: {
BUILD_VERSION: JSON.stringify("1.2.3"),
BUILD_TIME: JSON.stringify("2024-01-15T10:30:00Z"),
},
});
```
Bun 会在构建时将这些常量内联到二进制文件中,因此它们不会产生任何运行时开销,并且能够实现死代码消除。
如需了解更多示例和模式,请参阅[构建时常量指南](/guides/runtime/build-time-constants)。
***
## 生产环境部署
编译后的可执行文件降低内存使用并提升 Bun 启动速度。
通常情况下,Bun 会在 `import` 和 `require` 时读取并转译 JavaScript 和 TypeScript 文件。这正是 Bun 能够“开箱即用”的原因之一,但这并非没有代价:从磁盘读取文件、解析路径、解析代码、转译以及打印源代码都会消耗时间和内存。
编译后的可执行文件将这些开销从运行时转移到了构建时。
部署到生产环境推荐做法:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
outfile: "./myapp",
},
minify: true,
sourcemap: "linked",
});
```
### 字节码编译
为提升启动速度,启用字节码编译:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./path/to/my/app.ts"],
compile: {
outfile: "./myapp",
},
minify: true,
sourcemap: "linked",
bytecode: true,
});
```
使用字节码编译,`tsc` 启动速度提升两倍:

字节码编译将大文件的解析开销从运行时转移到打包时,提升了启动速度,但稍微增加了 `bun build` 命令的时长,不影响源码可读性。
字节码编译在搭配 `--compile` 时同时支持 `cjs` 和 `esm` 格式。
### 各个标志的作用
`--minify` 参数会减小转译后输出代码的大小。对于大型应用,这可以节省数兆字节的空间。对于较小的应用,它仍可能略微改善启动时间。
`--sourcemap` 参数会嵌入使用 zstd 压缩的源映射,这样错误和堆栈跟踪就会指向其原始位置,而不是转译后的位置。发生错误时,Bun 会自动解压并解析源映射。
`--bytecode` 参数会启用字节码编译。每次在 Bun 中运行 JavaScript 代码时,JavaScriptCore(引擎)都会将源代码编译为字节码。`--bytecode` 会将这部分解析工作从运行时转移到打包时,从而缩短启动时间。
***
## 嵌入运行时参数
**`--compile-exec-argv="args"`** - 嵌入运行时参数,可在运行时通过 `process.execArgv` 获取:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./app.ts"],
compile: {
execArgv: ["--smol", "--user-agent=MyBot"],
outfile: "./myapp",
},
});
```
```ts app.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 在编译后的程序中
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]
```
### 通过 `BUN_OPTIONS` 传入运行时参数
独立可执行文件会读取 `BUN_OPTIONS` 环境变量,因此你可以无需重新编译即可传入运行时标志:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
# 在编译后的可执行文件上启用 CPU 性能分析
BUN_OPTIONS="--cpu-prof" ./myapp
# 启用堆性能分析并输出 Markdown 格式
BUN_OPTIONS="--heap-prof-md" ./myapp
# 组合多个标志
BUN_OPTIONS="--smol --cpu-prof-md" ./myapp
```
***
## 自动加载配置
独立可执行文件可以自动从运行目录加载配置文件。默认:
* **禁用** 加载 `tsconfig.json` 和 `package.json` — 这些通常只在开发时需要,编译时已经用过了
* **启用** 加载 `.env` 和 `bunfig.toml` — 这些通常包含运行时配置,部署时可能有所不同
未来版本中,为了更确定的行为,可能会默认禁用 `.env` 和 `bunfig.toml` 加载。
### 运行时启用配置加载
如果您的可执行文件需要在运行时读取 `tsconfig.json` 或 `package.json`,请使用以下标志启用:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
# 启用 tsconfig.json 运行时加载
bun build --compile --compile-autoload-tsconfig ./app.ts --outfile myapp
# 启用 package.json 运行时加载
bun build --compile --compile-autoload-package-json ./app.ts --outfile myapp
# 同时启用
bun build --compile --compile-autoload-tsconfig --compile-autoload-package-json ./app.ts --outfile myapp
```
### 运行时禁用配置加载
要禁用 `.env` 或 `bunfig.toml` 以实现确定性执行:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
# 禁用 .env 加载
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp
# 禁用 bunfig.toml 加载
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp
# 禁用所有配置加载
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./app.ts"],
compile: {
// tsconfig.json 和 package.json 默认禁用
autoloadTsconfig: true, // 启用 tsconfig.json 加载
autoloadPackageJson: true, // 启用 package.json 加载
// .env 和 bunfig.toml 默认启用
autoloadDotenv: false, // 禁用 .env 加载
autoloadBunfig: false, // 禁用 bunfig.toml 加载
outfile: "./myapp",
},
});
```
***
## 作为 Bun CLI 使用
此功能自 Bun v1.2.16 起支持
设置 `BUN_BE_BUN=1` 环境变量,可以将独立可执行文件作为 `bun` CLI 本身运行。该可执行文件会忽略其打包的入口点,转而提供完整的 `bun` CLI。
例如,考虑一个由以下脚本编译而成的可执行文件:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
echo "console.log(\"you shouldn't see this\");" > such-bun.js
bun build --compile ./such-bun.js
```
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
[3ms] bundle 1 modules
[89ms] compile such-bun
```
通常,使用参数运行 `./such-bun` 时,会执行该脚本。
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
# 默认执行打包入口脚本
./such-bun install
```
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
you shouldn't see this
```
但是,使用 `BUN_BE_BUN=1` 环境变量后,它的行为就会像 `bun` 二进制文件一样:
```bash icon="terminal" terminal theme={"theme":{"light":"github-light","dark":"dracula"}}
# 通过环境变量让可执行文件行为等同于 `bun` CLI
BUN_BE_BUN=1 ./such-bun install
```
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
bun install v1.2.16-canary.1 (1d1db811)
Checked 63 installs across 64 packages (no changes) [5.00ms]
```
基于 Bun 构建的 CLI 工具可以利用此功能来安装软件包、打包依赖项或运行其他文件,而无需下载单独的二进制文件或安装 Bun。
## 全栈可执行文件
此功能自 Bun v1.2.17 起支持
`--compile` 标志可以创建一个同时包含服务器和客户端代码的独立可执行文件,非常适合全栈应用。当你在服务器代码中导入 HTML 文件时,Bun 会将前端资源(JavaScript、CSS 等)打包并嵌入可执行文件中。
```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { serve } from "bun";
import index from "./index.html";
const server = serve({
routes: {
"/": index,
"/api/hello": { GET: () => Response.json({ message: "Hello from API" }) },
},
});
console.log(`Server running at http://localhost:${server.port}`);
```
```html index.html icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
我的应用
你好,世界
```
```ts app.ts icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
console.log("Hello from the client!");
```
```css styles.css icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
body {
background-color: #f0f0f0;
}
```
构建为单个可执行文件:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile ./server.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./server.ts"],
compile: {
outfile: "./myapp",
},
});
```
生成的独立二进制包含:
* 你的服务器代码
* Bun 运行时
* 所有前端资源(HTML、CSS、JavaScript)
* 服务器用到的所有 npm 包
最终生成的是一个单独的文件,你可以将其部署到任何地方,而无需安装 Node.js、Bun 或任何依赖项:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
./myapp
```
Bun 会使用正确的 MIME 类型和缓存标头提供前端资源。HTML 导入会被替换为一个清单对象,`Bun.serve` 会使用该对象提供预先打包的资源。
有关构建全栈应用的更多信息,请参阅[全栈指南](/bundler/fullstack)。
***
## Worker
使用独立可执行文件中的 Worker,需要将 Worker 入口也加入构建:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile ./index.ts ./my-worker.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./index.ts", "./my-worker.ts"],
compile: {
outfile: "./myapp",
},
});
```
然后在代码中这样引用 Worker:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
console.log("Hello from Bun!");
// 以下方式均可:
new Worker("./my-worker.ts");
new Worker(new URL("./my-worker.ts", import.meta.url));
new Worker(new URL("./my-worker.ts", import.meta.url).href);
```
向独立可执行文件添加多个入口时,每个入口都会分别打包到可执行文件中。
我们最终可能会自动检测 `new Worker(path)` 中静态已知的路径并将其自动打包,但目前你需要将 Worker 文件列为入口,就像前面的示例一样。
如果你使用相对路径指向未包含在独立可执行文件中的文件,Bun 会相对于进程当前工作目录从磁盘加载该路径;如果文件不存在,则会报错。
***
## SQLite
使用 `bun:sqlite` 导入时,支持 `bun build --compile`。
默认情况下,数据库文件路径相对进程当前工作目录。
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import db from "./my.db" with { type: "sqlite" };
console.log(db.query("select * from users LIMIT 1").get());
```
这意味着,如果可执行文件位于 `/usr/bin/hello`,且用户的终端当前位于 `/home/me/Desktop`,Bun 会查找 `/home/me/Desktop/my.db`。
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
cd /home/me/Desktop
./hello
```
***
## 嵌入静态资源及文件
独立可执行文件可以直接将文件嵌入二进制文件中,因此单个可执行文件就可以携带应用程序所需的图像、JSON 配置、模板或其他资源。
### 机制原理
使用 `with { type: "file" }` [导入属性](https://github.com/tc39/proposal-import-attributes)来嵌入文件:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import icon from "./icon.png" with { type: "file" };
console.log(icon);
// 开发期间:"./icon.png"
// 编译后:"/$bunfs/root/icon-a1b2c3d4.png"(内部路径)
```
此导入返回**路径字符串**,指向嵌入文件。构建时 Bun 会:
1. 读取文件内容
2. 将数据嵌入可执行文件
3. 将导入替换为内部路径(以 `/$bunfs/` 为前缀)
你可以通过 `Bun.file()` 或 Node.js 的 `fs` API 读取嵌入文件。
### 用 Bun.file() 读取嵌入文件
`Bun.file()` 是读取嵌入文件的推荐方式:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";
// 以不同方式读取文件内容
const bytes = await file(icon).arrayBuffer(); // ArrayBuffer
const text = await file(icon).text(); // 字符串(文本文件)
const blob = file(icon); // Blob 对象
// 在响应中流式传输文件
export default {
fetch(req) {
return new Response(file(icon), {
headers: { "Content-Type": "image/png" },
});
},
};
```
### 用 Node.js fs 读取嵌入文件
嵌入文件可以与 Node.js 文件系统 API 一起使用:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import icon from "./icon.png" with { type: "file" };
import config from "./config.json" with { type: "file" };
import { readFileSync, promises as fs } from "node:fs";
// 同步读取
const iconBuffer = readFileSync(icon);
// 异步读取
const configData = await fs.readFile(config, "utf-8");
const parsed = JSON.parse(configData);
// 文件状态检查
const stats = await fs.stat(icon);
console.log(`Icon size: ${stats.size} bytes`);
```
### 实践示例
#### 嵌入 JSON 配置
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import configPath from "./default-config.json" with { type: "file" };
import { file } from "bun";
// 加载内置默认配置
const defaultConfig = await file(configPath).json();
// 尝试加载用户配置,失败则为空对象
const userConfig = await file("./user-config.json")
.json()
.catch(() => ({}));
const config = { ...defaultConfig, ...userConfig };
```
#### HTTP 服务器中提供静态资源
用 Bun.serve() 的 static 路由高效提供静态文件:
```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import favicon from "./favicon.ico" with { type: "file" };
import logo from "./logo.png" with { type: "file" };
import styles from "./styles.css" with { type: "file" };
import { file, serve } from "bun";
serve({
static: {
"/favicon.ico": file(favicon),
"/logo.png": file(logo),
"/styles.css": file(styles),
},
fetch(req) {
return new Response("Not found", { status: 404 });
},
});
```
Bun 会自动处理静态路由的 Content-Type 头和缓存策略。
#### 嵌入模板文件
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import templatePath from "./email-template.html" with { type: "file" };
import { file } from "bun";
async function sendWelcomeEmail(user: { name: string; email: string }) {
const template = await file(templatePath).text();
const html = template.replace("{{name}}", user.name).replace("{{email}}", user.email);
// 发送邮件,使用渲染后的模板...
}
```
#### 嵌入二进制文件
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import wasmPath from "./processor.wasm" with { type: "file" };
import fontPath from "./font.ttf" with { type: "file" };
import { file } from "bun";
// 加载 WebAssembly 模块
const wasmBytes = await file(wasmPath).arrayBuffer();
const wasmModule = await WebAssembly.instantiate(wasmBytes);
// 读取字体二进制数据
const fontData = await file(fontPath).bytes();
```
### 嵌入 SQLite 数据库
要将 SQLite 数据库嵌入编译后的可执行文件,请在导入属性中设置 `type: "sqlite"`,并将 `embed` 属性设置为 `"true"`。
数据库文件必须已存在于磁盘。然后在代码中导入:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };
console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());
```
最后编译为独立可执行文件:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile ./index.ts --outfile mycli
```
构建时数据库文件必须存在。`embed: "true"` 告诉打包器将数据库内容内嵌进可执行文件。常规运行 `bun run`
时数据库文件仍从磁盘加载。
在编译后的可执行文件中,嵌入的数据库为读写模式,但所有更改会在程序退出时丢失(内存存储)。
### 嵌入 N-API 插件
可将 `.node` 文件嵌入可执行文件:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const addon = require("./addon.node");
console.log(addon.hello());
```
如果使用 `@mapbox/node-pre-gyp` 或类似工具,必须直接 require `.node` 文件,否则无法正确打包。
### 嵌入目录
使用 `--asset`(或 JavaScript API 中的 `compile.assets`)将文件或目录树嵌入可执行文件,并保留其原始相对路径。嵌入的文件在运行时位于 `import.meta.dir` 下,并且可以通过 `node:fs`(`existsSync`、`statSync`、`readdirSync`、`readFileSync`)和 `Bun.file()` 访问。
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile ./index.ts --asset ./public --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
outfile: "./myapp",
assets: ["./public"],
},
});
```
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import fs from "node:fs";
import path from "node:path";
const publicDir = path.join(import.meta.dir, "public");
for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) {
console.log(entry.name, entry.isDirectory() ? "(dir)" : fs.statSync(path.join(publicDir, entry.name)).size);
}
const html = await Bun.file(path.join(publicDir, "index.html")).text();
```
多次传递 `--asset` 可嵌入多个目录(例如,对于 SvelteKit 构建,可以使用 `--asset ./client --asset ./prerendered`)。只有普通文件会被嵌入;目录树中的符号链接和空子目录会被跳过。
你也可以通过将单个文件添加为额外入口点,以旧方式嵌入文件;导入的资源会根据 `--asset-naming` 重命名(默认为 `[name]-[hash].[ext]`):
```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
import icon from "./public/assets/icon.png" with { type: "file" };
```
### 运行时检测独立模式
使用 `Bun.isStandaloneExecutable` 来检查当前进程是否正在从编译后的二进制文件运行:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
if (Bun.isStandaloneExecutable) {
// 从 `bun build --compile` 的输出运行
} else {
// 通过 `bun ` 运行,或作为库使用
}
```
与 `Bun.embeddedFiles.length > 0` 不同,这种检查不会为每个嵌入文件分配 `Blob` 对象,因此即使二进制文件嵌入了大量资源,也可以安全地在启动时调用。
### 列出嵌入文件
`Bun.embeddedFiles` 将所有嵌入文件以 `Blob` 对象的形式公开:
```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import "./icon.png" with { type: "file" };
import "./data.json" with { type: "file" };
import "./template.html" with { type: "file" };
import { embeddedFiles } from "bun";
// 列出所有嵌入文件
for (const blob of embeddedFiles) {
console.log(`${blob.name} - ${blob.size} bytes`);
}
// 输出示例:
// icon-a1b2c3d4.png - 4096 bytes
// data-e5f6g7h8.json - 256 bytes
// template-i9j0k1l2.html - 1024 bytes
```
`Bun.embeddedFiles` 中每个元素是带 `name` 属性的 `Blob`:
```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
embeddedFiles: ReadonlyArray;
```
可以使用它通过 `static` 路由提供所有嵌入的资源:
```ts server.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import "./public/favicon.ico" with { type: "file" };
import "./public/logo.png" with { type: "file" };
import "./public/styles.css" with { type: "file" };
import { embeddedFiles, serve } from "bun";
// 根据嵌入文件构造静态路由
const staticRoutes: Record = {};
for (const blob of embeddedFiles) {
// 去除文件名中的哈希:"icon-a1b2c3d4.png" -> "icon.png"
const name = blob.name.replace(/-[a-f0-9]+\./, ".");
staticRoutes[`/${name}`] = blob;
}
serve({
static: staticRoutes,
fetch(req) {
return new Response("Not found", { status: 404 });
},
});
```
`Bun.embeddedFiles` 不含源码文件(`.ts`、`.js` 等)以保护源码。
#### 内容哈希
默认情况下,嵌入文件的名称会附加内容哈希,这有助于通过 URL 或 CDN 提供文件时实现缓存失效。若要保留原始名称,请配置资源命名方式:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --asset-naming="[name].[ext]" ./index.ts
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
outfile: "./myapp",
},
naming: {
asset: "[name].[ext]",
},
});
```
***
## 代码压缩(Minification)
启用代码压缩以减少可执行文件体积:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --minify ./index.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
outfile: "./myapp",
},
minify: true, // 启用全部压缩
});
// 或更细粒度控制:
await Bun.build({
entrypoints: ["./index.ts"],
compile: {
outfile: "./myapp",
},
minify: {
whitespace: true,
syntax: true,
identifiers: true,
},
});
```
Bun 使用自身压缩器来减小代码体积。不过总体上 Bun 的二进制仍然偏大,未来还会优化。
***
## Windows 平台特有标志
在 Windows 上编译独立可执行文件时,平台特定选项可以自定义生成的 `.exe` 文件的元数据:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
# 自定义图标
bun build --compile --windows-icon=path/to/icon.ico ./app.ts --outfile myapp
# 隐藏控制台窗口(GUI 应用)
bun build --compile --windows-hide-console ./app.ts --outfile myapp
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./app.ts"],
compile: {
outfile: "./myapp",
windows: {
icon: "./path/to/icon.ico",
hideConsole: true,
// 额外的 Windows 元数据:
title: "My Application",
publisher: "My Company",
version: "1.0.0",
description: "A standalone Windows application",
copyright: "Copyright 2024",
},
},
});
```
Windows 可用选项说明:
* `icon` - 指定 `.ico` 图标文件路径
* `hideConsole` - 隐藏后台终端窗口(GUI 应用用)
* `title` - 可执行文件属性中的应用标题
* `publisher` - 发布者名称
* `version` - 版本号字符串
* `description` - 描述信息
* `copyright` - 版权声明
除了 `hideConsole` 之外,这些标志不能在交叉编译时使用,因为它们依赖 Windows
API。
***
## macOS 代码签名
为独立可执行文件进行代码签名以消除 Gatekeeper 警告,使用 `codesign` 命令:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
```
推荐附带带有 JIT 权限的 `entitlements.plist` 文件:
```xml icon="xml" title="info.plist" theme={"theme":{"light":"github-light","dark":"dracula"}}
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
com.apple.security.cs.disable-executable-page-protection
com.apple.security.cs.allow-dyld-environment-variables
com.apple.security.cs.disable-library-validation
```
使用 `--entitlements` 标志来支持 JIT 权限:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
```
签名后验证:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated Requirement
```
代码签名支持需要 Bun v1.2.4 及以上版本。
***
## 代码拆分
独立可执行文件支持代码拆分。结合 `--compile` 和 `--splitting` 可生成带有运行时动态加载代码拆分块的可执行文件。
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
bun build --compile --splitting ./src/entry.ts --outfile ./build/entry
```
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
await Bun.build({
entrypoints: ["./src/entry.ts"],
compile: true,
splitting: true,
outdir: "./build",
});
```
```ts src/entry.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
console.log("Entrypoint loaded");
const lazy = await import("./lazy.ts");
lazy.hello();
```
```ts src/lazy.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
export function hello() {
console.log("Lazy module loaded");
}
```
运行编译结果:
```bash terminal icon="terminal" theme={"theme":{"light":"github-light","dark":"dracula"}}
./build/entry
```
输出:
```txt theme={"theme":{"light":"github-light","dark":"dracula"}}
Entrypoint loaded
Lazy module loaded
```
***
## 使用插件
插件与独立可执行文件协同工作;使用它们在构建期间转换文件:
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import type { BunPlugin } from "bun";
const envPlugin: BunPlugin = {
name: "env-loader",
setup(build) {
build.onLoad({ filter: /\.env\.json$/ }, async args => {
// 将 .env.json 文件转换成已验证的配置对象
const env = await Bun.file(args.path).json();
return {
contents: `export default ${JSON.stringify(env)};`,
loader: "js",
};
});
},
};
await Bun.build({
entrypoints: ["./cli.ts"],
compile: {
outfile: "./mycli",
},
plugins: [envPlugin],
});
```
使用场景示例 — 构建时嵌入环境配置:
```ts cli.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import config from "./config.env.json";
console.log(`Running in ${config.environment} mode`);
console.log(`API endpoint: ${config.apiUrl}`);
```
插件可以执行任何转换:编译 YAML/TOML 配置、内联 SQL 查询、生成类型安全的 API 客户端,或预处理模板。请参阅[插件文档](/bundler/plugins)。
***
## 不支持的 CLI 参数
`--compile` 标志不支持以下标志:
* `--outdir` — 请改用 `outfile`。
* `--public-path`
* `--target=node`
* `--target=browser`(不含 HTML 入口点 — 对于使用 `.html` 文件的 `--compile --target=browser`,请参阅[独立 HTML](/bundler/standalone-html))
* `--no-bundle` - Bun 始终会将所有内容捆绑到可执行文件中。
***
## API 参考
`Bun.build()` 中的 `compile` 选项支持三种形式:
```ts title="类型" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
interface BuildConfig {
entrypoints: string[];
compile: boolean | Bun.Build.CompileTarget | CompileBuildOptions;
// ... 其他 BuildConfig 选项(minify、sourcemap、define、plugins 等)
}
interface CompileBuildOptions {
target?: Bun.Build.CompileTarget; // 交叉编译目标
outfile?: string; // 输出可执行文件路径
assets?: string[]; // 要嵌入到 import.meta.dir 下的文件/目录
execArgv?: string[]; // 运行时参数(process.execArgv)
autoloadTsconfig?: boolean; // 加载 tsconfig.json(默认:false)
autoloadPackageJson?: boolean; // 加载 package.json(默认:false)
autoloadDotenv?: boolean; // 加载 .env 文件(默认:true)
autoloadBunfig?: boolean; // 加载 bunfig.toml(默认:true)
windows?: {
icon?: string; // .ico 文件路径
hideConsole?: boolean; // 隐藏控制台窗口
title?: string; // 应用标题
publisher?: string; // 发布者名称
version?: string; // 版本字符串
description?: string; // 描述
copyright?: string; // 版权声明
};
}
```
用法示例:
```ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 简单布尔值 - 编译为当前平台,输出文件名为入口名
compile: true
// 目标字符串 - 交叉编译,输出文件名为入口名
compile: "bun-linux-x64"
// 详尽配置对象 - 指定输出文件和其他参数
compile: {
target: "bun-linux-x64",
outfile: "./myapp",
}
```
### 支持的目标
```ts title="Bun.Build.CompileTarget" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
type CompileTarget =
| "bun-darwin-x64"
| "bun-darwin-x64-baseline"
| "bun-darwin-arm64"
| "bun-linux-x64"
| "bun-linux-x64-baseline"
| "bun-linux-x64-modern"
| "bun-linux-arm64"
| "bun-linux-x64-musl"
| "bun-linux-arm64-musl"
| "bun-windows-x64"
| "bun-windows-x64-baseline"
| "bun-windows-x64-modern"
| "bun-windows-arm64";
```
### 完整示例
```ts build.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(build) {
// 插件实现
},
};
const result = await Bun.build({
entrypoints: ["./src/cli.ts"],
compile: {
target: "bun-linux-x64",
outfile: "./dist/mycli",
execArgv: ["--smol"],
autoloadDotenv: false,
autoloadBunfig: false,
},
minify: true,
sourcemap: "linked",
bytecode: true,
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
VERSION: JSON.stringify("1.0.0"),
},
plugins: [myPlugin],
});
if (result.success) {
console.log("构建成功:", result.outputs[0].path);
}
```
# 全栈开发服务器
Source: https://bun.zhcndoc.com/bundler/fullstack
使用 Bun 集成的开发服务器构建全栈应用,它能够打包前端资源并处理 API 路由
首先,导入 HTML 文件并将它们传递给 `Bun.serve()` 的 `routes` 选项。
```ts title="app.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { serve } from "bun";
import dashboard from "./dashboard.html";
import homepage from "./index.html";
const server = serve({
routes: {
// ** HTML 导入 **
// 将 index.html 打包并路由到 "/"
// 这使用 HTMLRewriter 扫描 HTML 中的 `