Bun 打包构建
Bun 内置了高性能的打包器(Bundler),可以将 JavaScript/TypeScript 代码打包用于浏览器或服务器。本章介绍 Bun 打包器的使用方法。
基本打包
命令行打包
bash
# 基本打包
bun build ./src/index.ts --outdir ./dist
# 指定输出文件
bun build ./src/index.ts --outfile ./dist/bundle.js
# 生成 source map
bun build ./src/index.ts --outdir ./dist --sourcemap
# 压缩代码
bun build ./src/index.ts --outdir ./dist --minifyAPI 打包
typescript
// build.ts
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
});
if (!result.success) {
console.error("构建失败:");
for (const log of result.logs) {
console.error(log);
}
} else {
console.log("构建成功!");
for (const output of result.outputs) {
console.log("输出:", output.path);
}
}构建选项
完整配置
typescript
const result = await Bun.build({
// 入口文件(可以多个)
entrypoints: ["./src/index.ts", "./src/worker.ts"],
// 输出目录
outdir: "./dist",
// 目标环境
target: "browser", // "browser" | "bun" | "node"
// 模块格式
format: "esm", // "esm" | "cjs" | "iife"
// 代码分割
splitting: true,
// 压缩
minify: true,
// 或分别设置
// minify: {
// whitespace: true,
// identifiers: true,
// syntax: true,
// },
// Source Map
sourcemap: "external", // "none" | "inline" | "external"
// 命名模式
naming: {
entry: "[name].[hash].js",
chunk: "[name]-[hash].js",
asset: "[name]-[hash][ext]",
},
// 外部依赖(不打包)
external: ["react", "react-dom"],
// 定义全局常量
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
__VERSION__: JSON.stringify("1.0.0"),
},
// 插件
plugins: [],
// 加载器配置
loader: {
".png": "file",
".svg": "text",
},
// 公共路径
publicPath: "/assets/",
// 根目录
root: "./src",
});目标环境
浏览器目标
typescript
await Bun.build({
entrypoints: ["./src/app.ts"],
outdir: "./dist",
target: "browser",
// 浏览器环境特定选项
format: "esm",
splitting: true,
minify: true,
});Node.js 目标
typescript
await Bun.build({
entrypoints: ["./src/server.ts"],
outdir: "./dist",
target: "node",
// Node.js 特定
format: "cjs", // 或 "esm"
external: ["*"], // 不打包 node_modules
});Bun 目标
typescript
await Bun.build({
entrypoints: ["./src/app.ts"],
outdir: "./dist",
target: "bun",
// Bun 运行时优化
});代码分割
自动代码分割
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
splitting: true, // 启用代码分割
});动态导入
typescript
// src/index.ts
// 动态导入会自动分割为单独的 chunk
const module = await import("./heavy-module.ts");
module.doSomething();
// 条件导入
if (needFeature) {
const feature = await import("./feature.ts");
feature.init();
}多入口分割
typescript
await Bun.build({
entrypoints: [
"./src/pages/home.ts",
"./src/pages/about.ts",
"./src/pages/contact.ts",
],
outdir: "./dist",
splitting: true,
});
// 共享代码会自动提取到公共 chunk资源处理
内置加载器
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
loader: {
// 作为文件输出(返回 URL)
".png": "file",
".jpg": "file",
".gif": "file",
".woff2": "file",
// 作为文本导入
".txt": "text",
".md": "text",
".html": "text",
// 作为 JSON 导入
".json": "json",
// 作为 Base64 数据 URL
".svg": "dataurl",
// 作为二进制数据
".bin": "binary",
// JavaScript/TypeScript
".js": "js",
".ts": "ts",
".jsx": "jsx",
".tsx": "tsx",
// CSS
".css": "css",
},
});导入资源
typescript
// 导入图片(返回 URL)
import logoUrl from "./logo.png";
const img = document.createElement("img");
img.src = logoUrl;
// 导入文本
import readme from "./README.md" with { type: "text" };
console.log(readme);
// 导入 JSON
import config from "./config.json";
console.log(config.version);CSS 处理
导入 CSS
typescript
// 导入 CSS 文件
import "./styles.css";
// CSS 模块(实验性)
import styles from "./component.module.css";
element.className = styles.container;CSS 打包
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
// CSS 会自动打包
});外部依赖
排除依赖
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
// 不打包这些依赖
external: ["react", "react-dom", "lodash"],
});排除所有 node_modules
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
// 使用通配符排除所有
external: ["*"],
});动态排除
typescript
import { dependencies } from "./package.json";
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
external: Object.keys(dependencies),
});环境变量替换
define 选项
typescript
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
"process.env.API_URL": JSON.stringify("https://api.example.com"),
__DEV__: "false",
__VERSION__: JSON.stringify("1.0.0"),
},
});代码中使用
typescript
// src/index.ts
if (process.env.NODE_ENV === "development") {
console.log("开发模式");
}
console.log("版本:", __VERSION__);
console.log("API:", process.env.API_URL);插件系统
插件接口
typescript
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(build) {
// 拦截模块解析
build.onResolve({ filter: /^virtual:/ }, (args) => {
return {
path: args.path,
namespace: "virtual",
};
});
// 拦截模块加载
build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => {
return {
contents: `export default "${args.path}"`,
loader: "js",
};
});
},
};
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
plugins: [myPlugin],
});YAML 插件示例
typescript
import { parse as parseYaml } from "yaml";
const yamlPlugin: BunPlugin = {
name: "yaml-loader",
setup(build) {
build.onLoad({ filter: /\.ya?ml$/ }, async (args) => {
const text = await Bun.file(args.path).text();
const data = parseYaml(text);
return {
contents: `export default ${JSON.stringify(data)}`,
loader: "js",
};
});
},
};
// 使用
import config from "./config.yaml";
console.log(config);环境变量插件
typescript
const envPlugin: BunPlugin = {
name: "env-plugin",
setup(build) {
build.onResolve({ filter: /^env:/ }, (args) => {
return {
path: args.path.slice(4), // 移除 "env:" 前缀
namespace: "env",
};
});
build.onLoad({ filter: /.*/, namespace: "env" }, (args) => {
const value = Bun.env[args.path] || "";
return {
contents: `export default ${JSON.stringify(value)}`,
loader: "js",
};
});
},
};
// 使用
import apiKey from "env:API_KEY";构建脚本
package.json 配置
json
{
"scripts": {
"build": "bun run build.ts",
"build:dev": "bun run build.ts --mode development",
"build:prod": "bun run build.ts --mode production"
}
}完整构建脚本
typescript
// build.ts
const mode = Bun.argv.includes("--mode")
? Bun.argv[Bun.argv.indexOf("--mode") + 1]
: "production";
const isDev = mode === "development";
console.log(`构建模式: ${mode}`);
// 清理输出目录
await Bun.$`rm -rf dist`;
// 构建客户端代码
const clientResult = await Bun.build({
entrypoints: ["./src/client/index.tsx"],
outdir: "./dist/public",
target: "browser",
format: "esm",
splitting: true,
minify: !isDev,
sourcemap: isDev ? "inline" : "external",
define: {
"process.env.NODE_ENV": JSON.stringify(mode),
},
naming: {
entry: isDev ? "[name].js" : "[name].[hash].js",
chunk: isDev ? "[name].js" : "[name].[hash].js",
},
});
if (!clientResult.success) {
console.error("客户端构建失败");
process.exit(1);
}
// 构建服务端代码
const serverResult = await Bun.build({
entrypoints: ["./src/server/index.ts"],
outdir: "./dist",
target: "bun",
format: "esm",
minify: !isDev,
external: ["*"], // 不打包依赖
});
if (!serverResult.success) {
console.error("服务端构建失败");
process.exit(1);
}
// 复制静态文件
await Bun.$`cp -r ./public/* ./dist/public/`;
console.log("构建完成!");
console.log("客户端文件:");
clientResult.outputs.forEach(o => console.log(" ", o.path));
console.log("服务端文件:");
serverResult.outputs.forEach(o => console.log(" ", o.path));与其他工具对比
速度对比
打包 React 应用(含依赖):
┌────────────────────────────────────────────┐
│ webpack ██████████████████████████ 12s │
│ esbuild ████ 1.5s │
│ bun ███ 1.1s │
└────────────────────────────────────────────┘功能对比
| 功能 | Bun | esbuild | webpack | Vite |
|---|---|---|---|---|
| 速度 | 极快 | 极快 | 慢 | 快 |
| 配置复杂度 | 低 | 低 | 高 | 中 |
| 插件生态 | 成长中 | 有限 | 丰富 | 丰富 |
| 代码分割 | ✅ | ✅ | ✅ | ✅ |
| HMR | ✅ | ❌ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | 需插件 | ✅ |
小结
本章介绍了:
- ✅ 命令行和 API 打包方式
- ✅ 构建选项和目标环境
- ✅ 代码分割和资源处理
- ✅ 外部依赖和环境变量
- ✅ 插件系统
- ✅ 完整构建脚本示例
下一步
继续阅读 测试运行器 了解 Bun 的内置测试框架。