news 2026/9/1 6:34:58

Bun v1.3 全栈运行时实战:从零搭建到踩坑记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bun v1.3 全栈运行时实战:从零搭建到踩坑记录

简介:一个围绕Bun v1.3全栈JavaScript运行时发布的源码示例包,适合全栈开发者、Node.js迁移者以及希望低成本评估新型JS运行时能力的团队。压缩包共5个文件,包含3个HTML演示页、1个InsCode配置文件和1个gitignore文件,整体仅11KB,结构精简易读。演示页覆盖了Bun v1.3的前端热重载、生产构建、内置MySQL/PostgreSQL/SQLite数据库客户端、Redis客户端、WebSocket优化等核心特性,可直接在浏览器中打开对比效果;配置文件则有助于快速搭建体验环境,便于读者验证Bun在吞吐量、并发处理等方面相对第三方方案的优势。目前已有116人学习下载,适合需要快速上手Bun、进行技术选型评估或迁移Node.js项目时参考借鉴。 上周我把一个跑了大半年的 Node.js 全栈项目迁到 Bun v1.3 上,整个过程比预想顺很多:依赖从 node_modules 三层深拷贝变成 bun install 的一次性解压,测试从 jest 的配置地狱切到 bun test 开箱即用,前后端从两个端口两个进程变成同一个 Bun.serve 撑起来。这篇文章不吹 Bun 多伟大,而是把 v1.3 这一版作为全栈 JS 运行时的定位、实际用法和踩坑记录整理出来。适合正在评估"前端转全栈""要不要把手头项目切 Bun"的开发者,以及已经在本地试过 Bun、想把它推到真实项目里的人。

1. 为什么说 Bun v1.3 能叫"全栈"运行时:先看清定位

1.1 一个进程里装下整个工具链,才是全栈的关键

我见过很多朋友对"全栈 JS 运行时"的第一反应是:不就比 Node 快一点嘛。这个理解其实窄了。一个典型 Node 全栈项目,光工具链就要装 node、npm 或 pnpm、webpack 或 vite、ts-node 或 esbuild、jest 或 vitest、nodemon,每个组件自带配置文件、依赖树和升级节奏,前端一套后端一套,中间还有构建产物、环境变量、路径兼容这些破事。

Bun 从 1.0 开始就在做一件事:把运行时、包管理器、打包器、转译器、测试器和热重载全部收进同一个二进制文件。bun install、bun test、bun run、bun build、bun pm 全是内置命令,不需要额外安装任何东西。到了 v1.3,这个拼图基本闭合了——你用它跑后端 API、跑前端构建、跑集成测试、跑静态文件托管,全程只有一个依赖。这才是"全栈运行时"的真正含义:不是能在服务器上跑 JS,而是从写代码到上线,一整条链路里没有第二个运行时。

1.2 它和 TypeScript、Node 生态之间是什么关系

Bun 默认支持 TypeScript,不需要 tsconfig、不需要单独装 typescript 编译器,更不需要 ts-node 这种胶水层。你写const n: number = 1直接bun run index.ts就能跑。对很多前端转全栈的朋友来说,这省掉了一整类"JS 和 TS 配置打架"的问题。但注意,Bun 不是"又一个 JS 运行时",它本身是 Node.js 生态的超集思路——你原来写的 Express、Koa、Prisma 代码,大部分可以直接在 Bun 里跑,因为 v1.3 对 Node 内置模块的兼容已经做得相当全面。这一点后面我会专门讲坑。

另外要澄清一个概念:Bun 和 Deno 在哲学上有本质差异。Deno 强调"重新发明规范",Bun 走的是"兼容 Node、替代 Node"的路线。这决定了它作为全栈平台迁移成本极低,你可以把现有 Express 项目一步步切进来,而不是推翻重写。我个人判断,这也是 Bun 在社区里升温比 Deno 快的原因。

2. 从零把一个前后端项目跑起来:核心动作拆解

2.1 安装、升级与卸载,一起说清楚

安装就一行:

curl -fsSL https://bun.sh/install | bash

装完重启终端,bun --version验证。升级用:

bun upgrade

很多热搜词里总有人问 "bun 怎么卸载",我在本地也折腾过好几次。Bun 卸载的方式非常粗暴但有效 —— 删目录:

rm -rf ~/.bun

然后打开你的 shell 配置文件(.bashrc.zshrc.profile),把里面对~/.bun/bin的 PATH 导出那一行删掉。Windows 用户去用户目录删.bun文件夹,同时检查环境变量。这个操作不会有特殊残留,因为 Bun 不写系统注册表,不装全局服务,这一点比很多开发工具干净得多。

2.2 一个进程同时扛 API 和静态资源

Bun 的全栈体验核心在Bun.serve。它不仅能起 HTTP 服务,还能直接声明静态路由和 API 路由。这里我直接给一个刚初始化完就能跑的最小例子:

mkdir bun-notes && cd bun-notes bun init -y

然后写server.ts

const server = Bun.serve({ port: 3000, static: { "/": new Response("Hello from Bun!", { headers: { "Content-Type": "text/html" }, }), }, routes: { "/api/ping": () => Response.json({ pong: true }), }, }); console.log(`listening on http://localhost:${server.port}`);

bun run server.ts,打开 localhost:3000 看到页面,再访问 /api/ping 拿到 JSON。就这么简单,一个进程里前后端都活了。老手一眼能看出来好处:没有跨域问题、没有端口协调问题、没有两套服务部署问题,本地联调效率直接上一个台阶。

2.3 路由参数、请求体和响应工具的几个细节

routes 里支持路径参数,写法是冒号开头:

routes: { "/api/users/:id": { GET: (req) => Response.json({ userId: req.params?.id }), DELETE: (req) => new Response("deleted", { status: 204 }), }, }

处理请求体时有个容易踩的坑:req.json()只能调用一次,调用完 body 流就消耗完了。如果你既想读原始文本又想做 JSON 解析,得先用await req.text(),再手动JSON.parse。另外Bun.serve里有 body 大小限制,默认 128MB,改法是maxRequestBodySize字段,这个对上传文件场景很重要。

3. 这一版里最值得吃的几个能力点

3.1 HTMLRewriter:服务端改 HTML 的瑞士军刀

Bun 内置了 HTMLRewriter,这玩意儿在 Node 生态里是个稀缺品。它能用类似 jQuery 选择器的方式对 HTML 做流式转换,不用把整个页面解析成 DOM。做微前端、做页面注入、做 SEO 改造特别好用:

import indexHtml from "./index.html"; const home = new HTMLRewriter() .on("title", { text(text) { text.replace("Bun Notes 官方示例"); }, }) .transform(indexHtml);

这里import indexHtml from "./index.html"不是魔法,是 Bun 在编译期自动把 HTML 转成文本模块。用 HTMLRewriter. transform 之后,你可以把处理完的 HTML 直接塞进静态路由。我在项目里就是靠它统一给所有页面注入统计脚本,不用动模板引擎。

3.2 Node.js 兼容面:现在能跑很多以前跑不了的包

v1.3 在 Node 兼容层上下了不少功夫,重点补了node:httpnode:httpsnode:fsnode:crypto等模块的边界情况。实测我原来用的 Express 4 中间件几乎全部能跑,Prisma 在 Linux 和 macOS 上也能正常工作。但要注意,兼容不等于完全等价,比如node:net底层和依赖原生 socket 特性的库(某些数据库驱动、SSH 库)偶尔会出问题,这类包大概率跑不起来。

我的建议是:先跑,别预先判死刑。Bun 生态已经过了"啥都用不了"的阶段,直接bun run试一试,报错再查https://bun.sh/docs/runtime/nodejs-apis的兼容清单。我迁项目时就是靠这个页面排查出两个坑,后面单独讲。

3.3 SQLite、S3 和内置 API:少装一堆 npm 包

Bun 把很多以前要靠第三方包的活都内置了。最典型的是bun:sqlite—— 一个同步的、零依赖的 SQLite 驱动,API 简洁,性能比很多 ORM 底层驱动还快。还有Bun.s3()客户端,给 AWS S3 兼容的对象存储提供了一套原生读写接口,不只是 AWS,MinIO、Cloudflare R2 这些兼容 S3 的服务都能连。这些东西对全栈项目的意义很大:你不需要为了一个数据库查询去拉一大堆依赖,整个项目依赖树直接瘦一圈。

另外Bun.$这个 shell 工具也值得一提,它能让你在 JS 里直接跑 shell 命令并拿到结果,做部署脚本、做预处理任务都很顺手。全栈项目里经常要写文件操作、跑外部命令,用Bun.$能少写一大坨 child_process 样板代码。

4. 实战:用 Bun 写一个可运行的 notes 全栈小应用

光讲能力不落地没用。下面我完整带一遍:一个笔记应用,后端是 SQLite 存的 CRUD API,前端是一个原生 HTML 页面,跑在同一个 Bun.serve 上。

4.1 项目结构设计

bun-notes/ ├── server.ts # 服务入口,API + 静态资源 ├── server.test.ts # 集成测试 ├── package.json └── tsconfig.json # 可选,不写也能跑

这个结构有意保持极简。真实项目里你可能会拆 handlers、db、lib,但核心思想是:入口文件同时声明静态路由和 API 路由。

4.2 后端 API + SQLite 数据层

// server.ts import { Database } from "bun:sqlite"; const db = new Database("notes.db", { create: true }); db.run(` CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, body TEXT, created_at TEXT DEFAULT (datetime('now')) ) `); // 演示用:每次启动清空数据 db.run("DELETE FROM notes"); const listNotes = () => { const rows = db.query("SELECT * FROM notes ORDER BY id DESC").all(); return Response.json(rows); }; const createNote = async (req: Request) => { const { title, body } = await req.json(); if (!title) return Response.json({ error: "title is required" }, { status: 400 }); db.run("INSERT INTO notes (title, body) VALUES (?, ?)", [title, body ?? ""]); return Response.json({ ok: true }, { status: 201 }); }; const deleteNote = (req: Request) => { const id = (req.params as { id: string }).id; db.run("DELETE FROM notes WHERE id = ?", [id]); return Response.json({ ok: true }); }; Bun.serve({ port: 3000, static: { "/": new Response(indexHtml(), { headers: { "Content-Type": "text/html; charset=utf-8" }, }), }, routes: { "/api/notes": { GET: listNotes, POST: createNote, }, "/api/notes/:id": { DELETE: deleteNote, }, }, }); function indexHtml() { return `<!doctype html> <html lang="zh-CN"> <head> <meta charset="utf-8" /> <title>Bun Notes</title> <style>body{font-family:system-ui;max-width:720px;margin:48px auto;padding:0 16px} #list div{border-bottom:1px solid #eee;padding:12px 0} button{margin-left:8px}</style> </head> <body> <h1>Bun Notes</h1> <form id="form"> <input name="title" placeholder="标题" required /> <input name="body" placeholder="内容" /> <button type="submit">添加</button> </form> <div id="list"></div> <script type="module"> const list = document.getElementById("list"); const form = document.getElementById("form"); async function load() { const data = await fetch("/api/notes").then(r => r.json()); list.innerHTML = ""; for (const n of data) { const div = document.createElement("div"); div.innerHTML = "<strong>" + n.title + "</strong> " + n.body; const del = document.createElement("button"); del.textContent = "删除"; del.onclick = async () => { await fetch("/api/notes/" + n.id, { method: "DELETE" }); load(); }; div.appendChild(del); list.appendChild(div); } } form.onsubmit = async (e) => { e.preventDefault(); const fd = new FormData(form); await fetch("/api/notes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: fd.get("title"), body: fd.get("body") }) }); form.reset(); load(); }; load(); </script> </body> </html>`; } console.log("listening on http://localhost:3000");

bun run server.ts之后,打开页面添加几条笔记,再新建一个终端跑curl http://localhost:3000/api/notes,能直接看到 JSON 数据。整个过程没有配 CORS、没有起两个服务、没有装任何 ORM,这就是全栈运行时该有的样子。

4.3 跑测试和热更新

Bun 内置测试器,集成测试直接写:

// server.test.ts import { describe, expect, test } from "bun:test"; const BASE = "http://localhost:3000"; describe("notes api", () => { test("创建笔记后能在列表里查到", async () => { await fetch(`${BASE}/api/notes`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: "第一篇", body: "hello bun" }), }); const list = await fetch(`${BASE}/api/notes`).then((r) => r.json()); expect(list.some((n: any) => n.title === "第一篇")).toBe(true); }); });

bun test,Bun 会自动把测试探测出来执行。这里要提醒一句:先手动启动服务再跑测试,因为测试脚本本身不负责起服务。想更省事可以写个 beforeAll 在测试里 spawn 服务进程,但初学者我建议先保持简单。

开发时用bun --hot server.ts启动,修改文件后不用重启,Bun 会基于模块图做局部热更新。这个热更新不是简单重启进程,而是尽量复用没变的模块,实测改一个 API handler 之后,已有连接不会断开。提交生产前用bun build ./server.ts --compile --outfile bun-notes-app,能打出一个包含运行时和代码的单一可执行文件,目标机器不用装 Bun,我拿它做小工具分发给同事,体验非常好。

5. 实际跑起来之后,值得说的坑和处理经验

5.1 两个 Node 兼容性的坑

第一个坑是回调式 API。Bun 对node:fs的 Promise 版本支持很完善,但某些老库还在用 callback 风格,比如传入一个(err, data) => {}回调。v1.3 大部分情况能兜住,但个别极端参数组合会直接抛 "运行时错误"。我的处理方式是:优先用内置的Bun.file()读写文件,或者改用 promise 版 API,别跟老库硬刚。

第二个坑是process.env的读取时机。Bun 在启动时读取.env文件并把变量合并进环境,但如果你在某个模块顶层就读取环境变量,而这个模块之前被其他测试用例带加载过,缓存可能导致变量读不到最新值。解决方案是不要在模块顶层解构整个process.env,而是在用的时候再读,或者统一走一个config.ts在入口函数里初始化。

5.2 运行时错误的完整排查链路

全栈项目最常见的生产报错是连接问题。我踩过的一个具体案例:API 有时候返回 500,日志只有一行 "error: connection closed prematurely"。我当时的排查链路是这样的:

  1. 先确认是不是代码问题:本地bun --hot server.ts复现,发现本地稳定跑几个小时没问题。
  2. 排查是不是部署环境网络配置问题:检查了服务端口监听、防火墙,没发现异常。
  3. 抓生产进程的状态:用ctrl + c杀掉进程后看日志尾部,发现是日志输出进程先行退出,导致外层服务误报连接关闭。
  4. 最终确认是进程管理器把 stdout 管道 buffer 满了,服务端还没崩,观察者先以为崩了。

排查结果是:不是 Bun 的 bug,是我自己的进程管理脚本没处理子进程的退出状态。我想说的是,Bun 的报错一般比较直白,看到 "panic" 或 "Segmentation fault" 用bun upgrade升级一下版本,大概率是修复了某个已知问题;看到网络类错误,优先怀疑自己的部署层,别急着甩锅给运行时。

5.3 什么时候适合把项目迁到 Bun

最后说点主观判断。经过这一轮迁移,我认为这几类项目最值得切 Bun:一是全栈小团队的项目,人员本来就要写前端和后端,Bun 能砍掉一半工具链学习成本;二是对冷启动敏感的服务,Bun 的启动速度比 Node 快一个数量级,Serverless 场景尤其划算;三是想给团队推广 TypeScript 的项目,Bun 零配置跑 TS,能让"JS 还是 TS"的争论直接消失。

不太适合的也有两类:重度依赖原生 Node 扩展(node-gyp 编译出的 .node 模块)的项目,因为 Bun 的 native ABI 兼容还不完美;以及已经深度用上 pnpm workspace + 复杂 monorepo 工具链的项目,迁移收益有限,除非你想要单一二进制部署。

我在实际使用里学到最重要的一招:迁移时不要一把梭,先挑一个非核心服务,用bun run启动看看日志有没有兼容告警,再逐步扩大范围。Bun v1.3 的兼容层已经足够成熟,但"足够成熟"和"完全等价"之间,永远差一个你的业务边界。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 6:34:54

iVISSA特征波段筛选算法:原理、实现与光谱建模实战

简介&#xff1a;本资源是一套面向遥感、农业与环境科学领域科研人员及高年级本科生的光谱特征波段筛选工具包&#xff0c;聚焦解决高维光谱数据冗余严重、建模效率低、关键波段识别难等实际问题。压缩包共12个文件&#xff08;9个MATLAB脚本.m、2个.mat数据文件、1个license.t…

作者头像 李华
网站建设 2026/9/1 6:33:30

Qwen3-TTS实战:开源语音合成模型的集成与工程化指南

简介&#xff1a;面向需要快速上手多语种语音合成的开发者、内容创作者及零基础学习者&#xff0c;这份资源以 Qwen3-TTS 为核心&#xff0c;系统讲解语音速度、音高、音色三维独立调节的核心功能&#xff0c;以及支持十种主要语言的跨语言合成能力。无论是否具备深厚技术背景&…

作者头像 李华
网站建设 2026/9/1 6:31:25

深度强化学习智能决策系统:从原理到工程实践指南

简介&#xff1a;基于深度强化学习的智能决策系统源码包&#xff0c;面向计算机与人工智能领域的学生、研究者和算法工程师&#xff0c;解决复杂环境下的序贯决策问题&#xff0c;可应用于游戏人工智能、机器人控制等典型场景。包内包含可运行的训练代码脚本、已训练好的模型权…

作者头像 李华
网站建设 2026/9/1 6:31:07

2023秋招小红书数据岗笔试复盘:题型拆解与备战策略

2023秋招小红书数据岗笔试复盘&#xff1a;三种题型&#xff0c;两套解法&#xff0c;一份完整破题思路 每年秋招&#xff0c;数据岗笔试都是刷人最狠的一关。尤其是大厂的数据分析、数据科学类岗位&#xff0c;投递人数多、岗位名额少&#xff0c;笔试题目往往不是单纯考“会不…

作者头像 李华
网站建设 2026/9/1 6:29:13

几小时课程素材怎么管理?代理、标记、分类与同步验收

几小时课程素材整理&#xff0c;建议按“可追溯目录—单章节代理测试—章节/机位/音频分类—片头片中片尾同步验收”推进&#xff0c;而不是把所有文件一次拖进时间线。剪映中的具体入口可能随版本、端别和文件类型变化&#xff0c;所以开始处理整批素材前&#xff0c;先用一个…

作者头像 李华
网站建设 2026/9/1 6:28:22

飞牛NAS搭建网络启动U盘

目录 一、开启Docker支持 二、文件夹准备 三、项目构建 四、启动项目 五、配置 1.中文显示 2.选择网卡 3.配置DHCP服务 六、准备ISO启动镜像 七、启动服务 八、电脑启动 今天装好的一台NAS计划下午要送过去,客户恰好来电话说另外的一件事儿,我正好在NAS上装了个应…

作者头像 李华