尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Bun 实战指南:一个命令搞定 JavaScript 运行时、包管理、测试与构建

Bun 实战指南:一个命令搞定 JavaScript 运行时、包管理、测试与构建 写前端或后端项目时很多人的日常已经被 Node.js 生态固化成了“打开终端 → npm install → 写代码 → 跑脚本”这条流程。这套流程没有问题但它最大的成本恰恰是“流程之外”的东西依赖装一半失败、不同包管理器生成不同锁文件、测试框架和构建工具又要单独安装。近期我持续使用 Bun 之后最直观的感受是它把运行时、包管理器、测试器、构建器全部放进同一个命令里很多日常任务确实被简化了。本文就从 Bun v1.4 这个版本节点出发带你完整走一遍安装、核心概念、实战项目、内存错误排查与工程最佳实践。1. Bun 是什么新一代 JavaScript 全家桶1.1 为什么需要 BunBun 是一个 JavaScript 运行时和 Node.js、Deno 处于同一赛道。它由 Jarred Sumner 发起底层使用 WebKit 的 JavaScriptCore 引擎而不是 Node.js 使用的 V8 引擎。JavaScriptCore 的启动速度和内存表现本身就有优势Bun 还使用 Zig 语言编写底层代码这让它在进程启动、文件系统操作、HTTP 解析等场景下比 Node.js 快不少。不过“快”不是 Bun 唯一的目的。Bun 更像是一个“全家桶”式工具链运行时直接运行 JavaScript、TypeScript、JSX。包管理器自带bun install兼容 npm 生态。测试器自带bun test不需要单独安装 Jest 或 Vitest。构建器自带bun build可以打包前端或后端代码。也就是说过去一个新项目要装“node npm jest webpack/vite”今天用 Bun 一个工具就能覆盖大部分工作。对个人项目、快速原型、后端接口开发来说这套组合能省下非常多的环境维护成本。1.2 Bun 的核心特点从实际使用体验来看我认为 Bun 最值得关注的特点有三个。第一启动速度极快。Bun 启动一个脚本通常只需要几十毫秒相比 Node.js 动辄几百毫秒的启动时间在开发小工具、CLI 脚本、定时任务时体感差异非常明显。第二内置能力丰富。Bun 不需要额外安装nodemon、dotenv、cors这类基础库它原生支持环境变量、热重载--hot模式、.env文件读取、HTTP 服务、WebSocket、SQLite 等能力。第三TypeScript 原生支持。Bun 不需要ts-node、tsx这类工具也不需要预先编译直接执行.ts文件即可这让开发过程中少了一整层配置。1.3 适用场景与使用边界Bun 适合以下几类场景快速开发后端 API 或内部工具。替代 npm/pnpm作为包管理器使用。编写 TypeScript 脚本或 CLI 工具。学习 JavaScript/TypeScript希望快速看到运行结果。构建单文件可执行程序分发小工具。但也要注意Bun 和 Node.js 生态虽然兼容性越来越高却仍然不是 100% 等价。某些依赖原生模块的 Node 包、某些深度使用 V8 内部 API 的库在 Bun 下可能无法正常运行。生产环境中建议先在测试环境验证依赖和运行结果再决定是否全量切换。2. 环境准备与安装2.1 跨平台安装方式Bun 支持 macOS、Linux 和 Windows。在不同平台上安装方式如下。macOS / Linux 下推荐使用官方脚本curl -fsSL https://bun.sh/install | bashWindows 下推荐使用 PowerShellpowershell -c irm bun.sh/install.ps1 | iex如果你本机已经有 Node.js 环境也可以用 npm 安装全局版本npm install -g bun安装完成后脚本会把bun可执行文件放到~/.bun/bin目录下。如果终端提示找不到bun需要将该目录加入 PATH。macOS/Linux 可以执行export PATH$HOME/.bun/bin:$PATH建议把这一行写入~/.bashrc或~/.zshrc避免每次重启终端都要手动配置。2.2 验证安装与版本信息安装完成后先验证版本。bun --versionbun --version输出的是版本号例如1.4.x。如果需要查看当前安装对应的 git revision可以执行bun --revision这里需要提醒一下Bun 的版本迭代速度比较快不同于 Node.js 一个 LTS 版本通常维护两三年。Bun 几乎每个月都有版本更新安装时尽量保持最新稳定版。本文演示内容以 1.x 通用能力为主如果你使用的版本与示例有细微差异以实际输出为准。2.3 快速初始化项目用 Bun 初始化一个项目很简单mkdir bun-demo cd bun-demo bun init -ybun init -y会自动生成package.json、tsconfig.json、index.ts等基础文件。生成后的package.json大致如下{ name: bun-demo, version: 1.0.0, module: index.ts, type: module, devDependencies: { types/bun: latest } }和 npm 初始化的项目相比多了一个module: index.ts字段这是 Bun 自己的入口约定。直接用bun run index.ts就能启动项目。3. 核心能力拆解从运行时到工具链3.1 直接运行 TypeScript 脚本很多新手第一次感受到 Bun 的“爽”就是从直接运行 TypeScript 开始的。先创建一个hello.tsconst name Bun; const time new Date().toLocaleTimeString(); console.log(Hello, ${name}!); console.log(Current time: ${time});然后直接执行bun hello.ts输出类似Hello, Bun! Current time: 21:30:15这里不需要安装ts-node也不需要配置tsconfig.jsonBun 内部把 TypeScript 转译为 JavaScript 后直接运行。而且这个过程几乎无感知启动速度非常快。在实际项目中你还可以用bun run执行package.json中定义的脚本bun run dev这意味着原本写着dev: node server.js的脚本也可以改成dev: bun run server.ts由 Bun 来接管运行。3.2 包管理器 bun installbun install是 Bun 自带的包管理器兼容 npm 的node_modules目录结构也兼容package.json中的绝大部分字段。例如安装一个依赖bun add express安装开发依赖bun add -d types/express安装完成后项目下会生成锁文件。早期版本默认生成bun.lockb较新版本也会生成bun.lock。无论哪种形态都可以保证团队依赖版本的一致性。Bun 安装依赖的最大优势是快。它会使用全局缓存并做并发下载国内网络环境下可能比npm install快不少。如果你已经有一个用 npm 管理的项目也可以直接bun install试试通常不需要修改package.json。3.3 内置测试运行器 bun testBun 内置测试运行器风格类似 Jest 和 Vitest。先编写一个简单的数学函数math.tsexport function add(a: number, b: number): number { return a b; } export function multiply(a: number, b: number): number { return a * b; }再编写测试文件math.test.tsimport { describe, expect, test } from bun:test; import { add, multiply } from ./math; describe(math, () { test(add, () { expect(add(1, 2)).toBe(3); }); test(multiply, () { expect(multiply(3, 4)).toBe(12); }); });执行测试bun test测试成功后输出会显示通过的用例数量和耗时。整个过程不需要额外安装ts-jest、types/jest也不需要jest.config对于工具类项目来说足够轻量。如果你之前从 Vitest 迁移过来会发现大部分 API 是相似的。3.4 打包器 bun buildBun 还能作为构建器使用将源码打包成单文件。下面是一个入口文件build-demo.tsimport { add } from ./math; console.log(1 2 , add(1, 2));打包命令bun build ./build-demo.ts --outdir ./dist执行后会在dist目录下生成一个压缩后的 JS 文件。可以用node运行它比如node dist/build-demo.js也可以设置目标平台把产物打包为 Node 服务端代码bun build ./build-demo.ts --targetnode --outdir ./dist对于前端项目--targetbrowser是默认值。这个能力虽然还没有完全替代 Vite/Rollup 在生产级场景的所有细节但对小型前后端项目、CLI 工具、函数库来说已经足够用了。3.5 内置 HTTP 服务与 SQLiteBun 提供了内置的Bun.serveAPI可以像 Node.js 的http模块一样创建 HTTP 服务但写法更简洁const server Bun.serve({ port: 3000, fetch(request) { return new Response(Hello Bun); }, }); console.log(Server running at http://localhost:${server.port});执行后访问http://localhost:3000就能看到响应。这个 API 同时支持 WebSocket不需要单独引入ws库。Bun 还内置了 SQLite 模块bun:sqlite不需要安装better-sqlite3import { Database } from bun:sqlite; const db new Database(app.db, { create: true }); db.run( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL ); ); db.query(INSERT INTO users (name) VALUES (?)).run(Alice); const user db.query(SELECT * FROM users WHERE id ?).get(1); console.log(user);这就带来一个很大的想象空间用 Bun 写一个小型后端服务可以不需要额外安装任何数据库相关的依赖直接用内置 SQLite 做持久化快速上线一个 MVP 或内部工具。4. 完整实战用 Bun 构建一个短链接服务4.1 需求与结构设计现在我们把前面的核心能力串联起来做一个小而完整的项目短链接服务。需求比较简单用户提交一个长 URL服务返回一个短码。用户访问短地址时服务端 302 跳转到原始 URL。短码要尽量短支持重复访问。数据要持久化重启不丢失。我选择用 Bun 内置 HTTP 服务 内置 SQLite 实现。整个项目不需要安装任何第三方依赖。项目结构如下url-shortener/ ├── src/ │ ├── db.ts # SQLite 初始化 │ ├── shortener.ts # 短码生成与查询 │ ├── index.ts # HTTP 服务入口 │ └── shortener.test.ts # 单元测试 ├── package.json └── tsconfig.json4.2 创建项目与基础配置先创建项目目录mkdir url-shortener cd url-shortener bun init -ybun init -y会生成基础文件。然后编辑package.json将入口脚本改一下{ name: url-shortener, version: 1.0.0, module: src/index.ts, type: module, scripts: { dev: bun run --hot src/index.ts, start: bun run src/index.ts, test: bun test }, devDependencies: { types/bun: latest } }注意dev脚本中使用了--hot这是 Bun 的监听模式。修改文件后服务会自动重启和nodemon效果类似。4.3 编写数据库层 db.ts在src/db.ts中初始化 SQLiteimport { Database } from bun:sqlite; const db new Database(shortener.db, { create: true }); db.run( CREATE TABLE IF NOT EXISTS links ( id INTEGER PRIMARY KEY AUTOINCREMENT, short_code TEXT UNIQUE, original_url TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime(now)) ); ); // 开启 WAL 模式提升并发读写能力 db.run(PRAGMA journal_mode WAL;); export function getDb(): Database { return db; }这里的Database来自bun:sqlite。shortener.db是本地 SQLite 文件{ create: true }表示文件不存在时自动创建。short_code字段设置为 UNIQUE防止同一短码被重复插入。PRAGMA journal_mode WAL开启 WAL 模式后读操作不会阻塞写操作对接口服务更友好。4.4 编写短码生成逻辑 shortener.ts短码的核心思路是利用数据库自增 ID再把十进制 ID 转换成 62 进制字符串。import { getDb } from ./db; const BASE62 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; export function encodeBase62(num: number): string { if (num 0) return 0; let result ; while (num 0) { result BASE62[num % 62] result; num Math.floor(num / 62); } return result; } export function createShortUrl(originalUrl: string): string { const db getDb(); const result db .query(INSERT INTO links (original_url) VALUES (?)) .run(originalUrl); const id Number(result.lastInsertRowid); const shortCode encodeBase62(id); db.query(UPDATE links SET short_code ? WHERE id ?).run(shortCode, id); return shortCode; } export function getOriginalUrl(shortCode: string): string | null { const db getDb(); const row db .query(SELECT original_url FROM links WHERE short_code ?) .get(shortCode) as { original_url: string } | undefined; return row?.original_url ?? null; }为什么用 62 进制因为短码只会包含数字、小写字母、大写字母也就是0-9a-zA-Z一共 62 个字符。用户访问短链接时URL 中不会出现空格或特殊字符复制和识别都比较方便。这里我用lastInsertRowid拿到插入记录的自增 ID。有的数据库返回类型可能是 bigint所以包了一层Number()转换避免类型不一致的问题。4.5 编写 HTTP 服务入口 index.ts入口文件负责路由分发import { createShortUrl, getOriginalUrl } from ./shortener; const PORT 3000; const server Bun.serve({ port: PORT, async fetch(req) { const url new URL(req.url); // 首页提示 if (url.pathname /) { return new Response(Bun URL Shortener is running); } // POST /shorten 生成短码 if (url.pathname /shorten req.method POST) { const body await req.json(); const target body.url as string; if (!target || !/^https?:\/\/\S$/.test(target)) { return Response.json({ error: Invalid URL }, { status: 400 }); } const code createShortUrl(target); return Response.json({ shortCode: code, shortUrl: http://localhost:${PORT}/${code}, }); } // GET /:code 访问短链接 const codeMatch url.pathname.match(/^\/([A-Za-z0-9])$/); if (codeMatch) { const original getOriginalUrl(codeMatch[1]); if (original) { return Response.redirect(original, 302); } return new Response(Short link not found, { status: 404 }); } return new Response(Not Found, { status: 404 }); }, }); console.log(Server listening on http://localhost:${PORT});这段代码的关键点有三个使用Response.redirect(original, 302)实现 302 重定向这是 Web 标准 API不需要额外依赖。使用req.json()解析请求体Bun 原生支持。短码校验使用正则^\/[A-Za-z0-9]$只允许访问合法的短码路径。4.6 运行与接口测试启动服务bun run src/index.ts看到输出Server listening on http://localhost:3000然后使用curl生成短链接curl -X POST http://localhost:3000/shorten \ -H Content-Type: application/json \ -d {url:https://bun.sh/docs}预期响应{ shortCode: 1, shortUrl: http://localhost:3000/1 }再访问短链接使用-I查看响应头curl -I http://localhost:3000/1预期响应HTTP/1.1 302 Found Location: https://bun.sh/docs到这里一个可用的短链接服务已经跑起来了。每次生成新的短链接短码会逐步变成2、3、10、11……短码位数增长非常慢足以支撑大量数据。4.7 为短码生成逻辑补一个测试为了保证核心逻辑正确给encodeBase62和getOriginalUrl写几个测试import { describe, expect, test } from bun:test; import { encodeBase62 } from ./shortener; describe(encodeBase62, () { test(0 编码为 0, () { expect(encodeBase62(0)).toBe(0); }); test(62 编码为 10, () { expect(encodeBase62(62)).toBe(10); }); test(自增 ID 编码结果是稳定短码, () { expect(encodeBase62(1)).toBe(1); expect(encodeBase62(63)).toBe(11); }); });执行bun test预期输出中会显示三个测试用例全部通过。如果后续修改了短码生成算法这个测试可以立刻帮你发现回归问题。5. 常见问题与排查思路内存错误与兼容性5.1 高内存占用与 OOM 问题在日常使用 Bun 时遇到最多的一类问题就是内存相关。我最近也看到一些开发者反馈在 Windows 环境中使用 OpenCode 等 AI 编程辅助工具调用 Bun 执行任务时偶尔会出现out of memory或进程崩溃的现象。这类问题首先要区分是“系统本身内存不足”还是“Bun 进程内存异常增长”。系统内存不足时错误信息通常是Reached heap limit Allocation failed - JavaScript heap out of memory或者操作系统的 OOM Killer 直接把进程杀掉。排查时可以先观察内存趋势# Linux / macOS ps aux | grep bun # 查看某个 PID 的内存使用 ps -o pid,rss,vsz,cmd -p PIDWindows 下可以使用任务管理器查看进程内存或使用 PowerShellGet-Process bun | Select-Object Id, WorkingSet64, PrivateMemorySize64如果发现内存持续上升而不是稳定在一个水位大概率是代码中出现了内存泄漏比如无限向数组或 Map 中添加对象没有清理。大量使用闭包变量无法被 GC 回收。高并发请求下每个请求都持有大对象。5.2 Windows 下的 Bun 内存错误Bun 在早期版本对 Windows 的支持不够完善1.1 版本开始原生支持 Windows。如果 Windows 下运行 Bun 频繁遇到崩溃或内存错误建议依次尝试以下方法升级 Bun 到最新版本。版本迭代通常会修复内存管理相关 bug。bun upgrade检查项目目录是否被安全软件或 Windows Defender 实时扫描。大量文件 IO 被拦截可能会导致进程异常。尝试在 WSL2 中运行 Bun。WSL2 提供了更接近 Linux 的生产环境很多在 Windows 原生环境下出现的内存问题会消失。如果运行的是 AI 编程辅助工具调用的 Bun 脚本检查是否为工具本身重复创建进程导致的累计内存上升而不是 Bun 单次执行的问题。5.3 Node.js 模块兼容性问题Bun 可以安装 npm 生态的包但“能安装”不代表“一定能运行”。常见报错包括Module not found: xxx Native module not found原因通常是模块依赖了 Node.js 原生二进制文件例如node-gyp编译产物。这类模块在 Bun 下运行时可能因为 ABI 不兼容而崩溃。解决办法尽量选择纯 JavaScript 实现的库。优先找原生模块的替代品比如使用bun:sqlite替代better-sqlite3。查看 Bun 官方兼容性文档确认核心接口已支持。如果必须使用原生模块可以在测试环境充分验证后再上线。5.4 常见报错排查表为了方便查阅这里把几个高频问题汇总一下问题现象常见原因解决思路启动报EACCES权限错误端口被占用或目录无权限换端口启动检查目录权限out of memory大文件一次性读取或内存泄漏改用流式处理加内存监控Cannot find module依赖未安装执行bun installWindows 下进程崩溃Bun 版本过旧或原生模块不兼容升级 Bun检查原生依赖bun test找不到测试文件文件命名不符合约定使用.test.ts命名并检查目录SQLite 报database is locked并发写锁冲突开启 WAL 模式减少高频写入6. 最佳实践与工程建议6.1 使用锁文件与固定版本Bun 项目默认会生成锁文件建议提交到 Git 仓库确保团队和 CI 环境安装一致的依赖版本。如果使用bun add安装依赖不要手动修改版本号交给 Bun 统一处理即可。如果是公司内部项目建议在package.json中给核心依赖锁定范围例如dependencies: { express: ^5.1.0 }这样做既允许补丁升级又不会意外跳到不兼容的大版本。6.2 内存与性能优化的实操建议Bun 虽然速度快但不代表不需要优化。在大文件处理场景中最忌讳一次性把整个文件读入内存。推荐使用流式读取const file Bun.file(large.txt); const stream file.stream(); for await (const chunk of stream) { // 分块处理 }在处理大量并发请求时可以引入一个简单的并发控制队列避免同时创建过多数据库连接或文件句柄。另外SQLite 数据库要避免在请求处理函数中进行高频写入。短链接服务这种读写量不大的场景没问题但如果要做高并发写入建议使用 WAL 模式并对高频更新的表定时执行VACUUM或数据清理。6.3 生产环境部署建议生产环境不建议直接bun run src/index.ts因为这样需要服务器安装 Bun 环境。推荐两种部署方式。第一种是使用官方 Docker 镜像FROM oven/bun:1 WORKDIR /app COPY package.json bun.lock* ./ RUN bun install --production COPY src ./src EXPOSE 3000 CMD [bun, run, src/index.ts]第二种是编译为单文件可执行程序bun build --compile ./src/index.ts --outfile shortener执行后会在当前目录生成shortener可执行文件拷贝到任意同架构的 Linux 服务器上即可运行不需要安装 Node.js 或 Bun。这种方式非常适合分发 CLI 工具和内部服务。6.4 跟随版本节奏不要盲目追新Bun 的版本迭代非常快新功能几乎每个版本都有但这也意味着 API 存在变化的可能。建议开发环境使用最新稳定版生产环境固定一个验证过的版本避免因为自动升级导致行为变化。关注变化的最好方式是阅读官方 Release Notes。每次升级后至少跑一遍项目的bun test和核心冒烟测试确认没有回归再合并。7. 总结与学习路线7.1 本文回顾这篇实战笔记从 Bun 的背景讲起带你完成了安装、核心能力拆解并用 Bun 内置的 HTTP 服务与 SQLite 实现了一个完整的短链接服务。通过这个例子你应该能看到 Bun 的核心理念减少工具链割裂让开发者把注意力集中在业务逻辑上。同时我们也讨论了 Bun 在 Windows 环境下的内存错误、Node.js 模块兼容性等常见问题。遇到报错时先看错误信息发生的时机再看代码的资源和进程使用趋势按表格里的排查思路逐步定位大部分问题都能解决。7.2 下一步可以继续探索的方向如果你想把 Bun 用到更深建议按以下路线继续学习 Bun.serve 的 WebSocket 能力做实时推送服务。阅读bun:sqlite的事务与迁移写法完善数据层。了解 Bun 的插件系统自定义打包流程。尝试把现有 Node.js 项目逐步迁移到 Bun记录差异点。部署到 Docker 或使用--compile打包体验发布流程。Bun 还在快速演进中它的生态、兼容性和稳定性都会越来越好。建议你亲手把短链接服务跑一遍再试着改造成自己的项目比如加上访问计数、过期时间、自定义短码等功能。动手实践过后你才能真正感受到“一个命令完成全家桶”这种开发体验带来的效率提升。如果本文对你有帮助可以收藏备用后续遇到 Bun 相关的问题也可以在评论区交流。
返回列表