第一个 Bun 程序
本教程共 34 篇 · 第 3 篇 · 更新于 2026-08-06
本节目标:
- 用
bun run直接执行一个.ts/.tsx/.js文件,体会「免编译」- 学会用
bun repl做即时的表达式试验- 用
bun create或bun init拉起一个可运行的最小项目骨架- 理解 Bun 项目里
index.ts、package.json、tsconfig.json各自扮演的角色
安装好 Bun 之后,最直观的验证方式就是真的跑一段代码。Bun 的一大卖点正是「开箱即跑 TypeScript 和 JSX」,不需要你先配置 tsc、ts-node、构建步骤或者任何 loader。这一节我们就从一行命令开始。
3.1 直接运行一个文件
新建一个文件,比如 hello.ts,内容只有一行:
console.log("Hello via Bun!");
然后在文件所在目录执行:
bun run hello.ts
终端会立刻打印出 Hello via Bun!。注意这里我们用的是 .ts 文件,里面写了 TypeScript 的类型语法也能照常跑。例如:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("Bun"));
bun run 会在加载阶段用内置的转译器把类型标注「剥离」掉,再把纯 JavaScript 交给 JavaScriptCore 执行。整个过程对用户是透明的,你不需要先 tsc 编译、也不需要额外的运行时插件。
Note
bun run <文件>与直接bun <文件>效果相同,例如bun hello.ts。bun run这个写法更常见于「运行 package.json 里的脚本」,二者可以混用。
除了 TypeScript,Bun 也能直接执行 .js、.jsx、.tsx、.mjs、.cjs、.mts、.cts 等常见扩展名。JSX / TSX 的转译同样内置,不需要 Babel 或 esbuild 预处理。
Bun 默认把文件当作 ES 模块(ESM)处理,这与 Node.js 的现代默认行为一致。写 CommonJS 风格的 require() 也能跑,同一个项目里 ESM 与 CJS 可以混用。
所以不管你是从老项目迁过来,还是直接写新语法,基本不用为模块格式操心。
Tip想确认 Bun 成功加载了哪个文件、解析路径是什么,可以临时在代码里
console.log(import.meta.url)打印当前模块地址,或用console.log(__filename)/console.log(__dirname)查看文件与目录路径(这两个是 Node.js 兼容全局,Bun 同样支持)。
3.2 运行 package.json 里的脚本
在真实项目里,命令通常写在 package.json 的 scripts 字段中。bun run <脚本名> 会读取该字段并执行对应的命令。
{
"name": "my-app",
"module": "index.ts",
"scripts": {
"start": "bun run index.ts",
"dev": "bun --hot index.ts"
}
}
bun run start
# 等价于执行 scripts.start 里的 "bun run index.ts"
这与 npm run 的用法一致,但 bun run 启动一条脚本的固定开销小得多,官方基准里能差出一个数量级。原因是它省掉了 Node 进程与脚本包装那一层。
这只是启动开销的差异。业务代码本身跑多快,跟用哪个命令启动没有关系,别把这个倍数套到运行性能上。
Tip如果只是想临时执行一小段代码,可以用
bun -e或bun --eval:bun -e 'console.log(1 + 1)'加上
-p/
3.3 用 REPL 做即时试验
不想建文件时,可以用交互式 REPL(Read-Eval-Print Loop)直接敲代码:
bun repl
Welcome to Bun v1.3.14
Type .copy [code] to copy to clipboard. .help for more info.
> 1 + 1
2
> const greeting = "Hello, Bun!"
undefined
> greeting
'Hello, Bun!'
REPL 的几个实用特性:
- 原生支持 TypeScript 与 JSX:可以当场写带类型标注或 JSX 的表达式,Bun 会自动转译。
- 顶层
await:不用包在async函数里就能await一个 Promise。 - 语法高亮:输入时实时着色,长表达式更容易看出括号是否配对。
- 历史记录持久化:保存在
~/.bun_repl_history(最多 1000 条),跨会话可用Up/Down翻找。 - Tab 补全:按
Tab补全属性名或命令;未闭合的括号会自动换行续写。 - Node.js 全局可用:
require、module、__dirname、__filename都能用,且相对当前工作目录解析。
REPL 里还有两个特殊变量:
| 变量 | 含义 |
|---|---|
_ | 上一次表达式的结果 |
_error | 上一次抛出的错误 |
> 2 + 2
4
> _ * 10
40
常用命令以 . 开头:.help(帮助)、.exit(退出)、.clear(清屏)、.history(打印历史)、.load ./script.ts(载入文件)、.save ./session.txt(导出历史)、.editor(多行编辑模式,Ctrl+D 求值)、.copy <表达式>(复制结果到剪贴板)。
Tip非交互场景可以用
bun repl -e/-p以 REPL 语义求值后退出,比如bun repl -p "await fetch('https://example.com').then(r => r.status)",适合写进脚本或 CI 里做连通性探测。
3.4 用 bun create 拉起项目骨架
想从零开始一个更完整的项目,可以用 bun create(或先 bun init)。两者定位不同:
bun init <目录>:生成一个空的 Bun 项目骨架,交互式选择模板(Blank/React/Library)。bun create <模板>:从一个 React 组件、create-<模板>npm 包、GitHub 仓库或本地模板,生成完整项目。
最简单的空项目:
bun init my-app
cd my-app
bun run index.ts
bun init 会创建 package.json、tsconfig.json、index.ts、README.md、.gitignore 等文件,并在结束时自动执行 bun install 安装 @types/bun(Bun 的 TypeScript 类型声明)。如果一路回车接受默认值,等价于加 -y / --yes:
bun init -y my-app
Note
bun init是「非破坏性」的:多次运行不会覆盖你已有的文件,可以放心在已有目录里补生成配置。
如果你手头有一个 React 组件文件 MyComponent.tsx,bun create 还能把它变成一个带热重载的开发环境:
bun create ./MyComponent.tsx
Bun 会分析组件的模块图、收集依赖、生成 .html 与入口文件,并启动前端开发服务器。它也支持 TailwindCSS 与 shadcn/ui 的自动探测配置,但这些属于进阶用法,初学阶段了解即可。
3.5 从 GitHub 或 npm 模板起步
bun create 还能直接拉取远程模板:
# 从 npm 上的 create-<模板> 包
bun create remix
# 从 GitHub 仓库
bun create user/repo
bun create github.com/user/repo mydir
远程模板默认不会覆盖已有文件;如需覆盖加 --force。Bun 会下载模板、复制文件、执行 bun install、并初始化一个全新的 Git 仓库(可用 --no-git 跳过,--no-install 跳过安装)。
Warning本地模板的行为与远程不同:用
bun create <本地名>时,如果目标目录已存在,Bun 会直接删除整个目标目录再写入。本地模板放在$HOME/.bun-create/<名>(全局)或<项目>/.bun-create/<名>(项目级)。请务必注意目标目录里是否有重要文件。
3.6 项目里的三个关键文件
用 bun init 生成的项目,最重要的三个文件是:
index.ts —— 入口文件。Bun 默认按优先级寻找入口:package.json 的 module / main 字段,或存在的 index.{tsx,ts,jsx,js,mts,mjs}。
package.json —— 项目的元数据与脚本入口。Bun 会读取其中的 scripts、dependencies 等字段。一个最小示例:
{
"name": "my-app",
"module": "index.ts",
"type": "module",
"private": true,
"scripts": {
"start": "bun run index.ts"
},
"devDependencies": {
"@types/bun": "latest"
}
}
tsconfig.json —— 主要给编辑器提供类型提示与自动补全(比如 Bun 全局对象的类型)。Bun 运行时本身不依赖它来做转译,但会读取其中的 paths(路径别名)和 jsx 相关配置。建议的 compilerOptions 包含:
{
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"jsx": "react-jsx",
"types": ["bun"]
}
}
WarningBun 运行 TypeScript 只做转译、不做类型检查。也就是说,即使你的代码里有类型错误,只要能转译成合法 JavaScript,Bun 依然会跑。类型检查请交给编辑器或单独的
tsc --noEmit步骤。这是「免编译秒跑」的代价,也是设计取舍,并非 bug。
3.7 一个最小可运行示例
把 index.ts 换成下面这段,体验一下 Bun 内置的 HTTP 能力(无需安装任何框架):
const server = Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello from Bun!");
},
});
console.log(`Listening on ${server.url}`);
bun run index.ts
# 打开 http://localhost:3000 即可看到 "Hello from Bun!"
Bun.serve 是 Bun 自带的 HTTP 服务器 API(属于 Bun.* 原生 API)。这里先跑通即可,后面的章节会专门讲热重载与全栈开发服务器。
3.8 小结与常见误区
bun run 文件或bun 文件都能直接执行 TypeScript / JSX,无需预编译。bun repl适合做快速试验,支持 TS、顶层await、历史持久化。bun init生成空骨架,bun create从模板生成完整项目;二者都可选。tsconfig.json主要服务于编辑器,运行时转译不依赖它;但paths与jsx配置 Bun 会读取。- Bun 转译 TypeScript 不做类型检查,类型错误不会阻止运行。
Note如果编辑器里
Bun全局对象报类型错误,先确认已安装@types/bun(bun add -d @types/bun),且tsconfig.json的compilerOptions.types含"bun"。这不影响运行,只是编辑器提示问题。