Bun —— JavaScript 运行时的第三次内卷
普通课约 35 分钟
本课导读
你每天敲 npm run dev 启动本站项目时,代码其实跑在 Node.js 上。Bun 是 2022 年冒出来的挑战者,口号是"把 Node、npm、打包器、测试框架全换成我,还快几倍"。本课先补一块地基——"JS 引擎"和"JS 运行时"到底是什么关系(搞懂这个,Node/Deno/Bun 的一切八卦都能听懂了)——再讲清 Bun 凭什么快、你现在该不该用。预计 35 分钟。
地基:引擎 vs 运行时
JavaScript 这门语言自己不会读文件、不会开网络端口、连 console.log 都不是语言标准的一部分。语言规范只定义了语法、对象、Promise 这些"纯计算"的部分。真正让 JS 代码动起来的是两层东西:
第一层:引擎(Engine)——负责"纯计算"的执行器,把 JS 源码变成机器指令。世界上主要有三个,全是浏览器大战的产物:
| 引擎 | 出身 | 用在哪 |
|---|---|---|
| V8 | Google,为 Chrome 开发 | Chrome、Node.js、Deno |
| JavaScriptCore(JSC) | Apple,为 Safari 开发 | Safari、Bun |
| SpiderMonkey | Mozilla | Firefox |
第二层:运行时(Runtime)——引擎 + 一整套"跟外部世界打交道"的 API + 事件循环调度。浏览器是一种运行时(提供 DOM、fetch、localStorage);Node 是另一种运行时(提供 fs 读写文件、http 开服务器、process 环境变量)。
提示
类比:引擎是发动机,运行时是整车。V8 这台发动机装进 Chrome 就是浏览器,装进 Node 就是服务器运行时——同一台发动机,配的"轮子和方向盘"(API)完全不同。所以浏览器里没有 fs(不能让网页随便读你硬盘),Node 里没有 document(服务器上没有网页 DOM)。
三代运行时的恩怨史
Node.js(2009):Ryan Dahl 把 V8 从浏览器里拆出来、配上文件/网络 API——JS 从此能写服务器,前后端一门语言,直接催生了如今整个 npm 生态。你项目的一切(Next.js、ESLint……)都长在 Node 上。
Deno(2018):还是 Ryan Dahl,发表著名演讲《我对 Node 的十大后悔》后另起炉灶:默认禁止读文件/联网(安全沙箱)、原生跑 TypeScript、内置工具链。理念先进,但跟 npm 生态兼容不佳,起步艰难(后来妥协支持了 npm 包)。
Bun(2022):Jarred Sumner 用 Zig(一门 C 级别的系统语言,和 Rust 同一生态位的极简派)从零写的运行时,换用 Apple 的 JSC 引擎。它吸取了 Deno 的教训,把兼容性放在第一位:目标是 Node 的原地替换(drop-in replacement)——你的项目不改代码,理论上直接 bun run 就能跑。
Bun 的两张牌
第一张牌:快。 快在三处——运行时本身用 Zig 手工优化、JSC 启动比 V8 更快、以及最有体感的:bun install 装依赖比 npm 快几倍到几十倍(重写了整个安装器:并行下载、二进制 lockfile、全局缓存硬链接)。
第二张牌:all-in-one。 Node 时代你需要拼装一堆工具,Bun 全部内置:
| 你现在用的 | Bun 内置的 |
|---|---|
| node(运行时) | bun run |
| npm(包管理) | bun install |
| jest / vitest(测试) | bun test |
| esbuild / webpack(打包) | bun build |
| ts-node(跑 TS) | 原生支持,直接 bun index.ts |
# 体验一下"全家桶"的画风(Windows 上装 Bun:powershell -c "irm bun.sh/install.ps1 | iex") bun init # 初始化项目(替代 npm init) bun install # 装依赖(读的就是 package.json,秒级完成) bun run dev # 跑 package.json 里的脚本(替代 npm run dev) bun index.ts # 直接运行 TS 文件,不用配置任何编译 bun test # 跑测试,Jest 兼容语法
写个 HTTP 服务器感受下 API 风格(Bun 自带顶层 Bun 全局对象):
// server.ts —— 用 Bun 起一个 HTTP 服务器,零依赖零配置 // (Node 里同样的事要 import http 然后写一堆样板代码) Bun.serve({ port: 3000, fetch(req) { return new Response("Hello from Bun!"); }, }); console.log("跑起来了:http://localhost:3000"); // 运行:bun server.ts
注意 fetch(req) 返回 Response——Bun(和 Deno)尽量复用浏览器已有的 Web 标准 API(Request/Response/fetch),而不是像 Node 早期那样发明自己的一套。这是新一代运行时的共同审美:一门语言,一套 API,浏览器和服务器通用。
冷静区:该不该换?
兼容性现状:Bun 实现了绝大部分 Node API,多数 npm 包能直接跑,但"绝大部分"不是"全部"——一些依赖 Node 底层细节的包(原生插件、罕见 API)会踩坑。Next.js 这类重型框架官方生产支持仍以 Node 为准。
给你的实用建议(按风险从低到高):
- 今天就能用:拿 Bun 当"更快的 npm"——
bun install只是装依赖,装完照常用 Node 跑,纯提速不改运行时,风险约等于零 - 随手可用:写一次性脚本、小工具时
bun xxx.ts直接跑 TS,省去所有配置 - 再等等:把生产环境的 Next.js 运行时换成 Bun——收益不明显(瓶颈通常不在运行时),风险却真实存在
注意
听到"X 比 Y 快 30 倍"时永远追问一句:快在哪个环节,那个环节是我的瓶颈吗? bun install 快 20 倍是真的,但你一天装几次依赖?你网站的慢来自数据库查询和网络往返,换运行时救不了。识别 benchmark 营销,是读懂技术圈的基本功。
够用判断线
下次群里聊 Bun,你的知识储备:Bun = Zig 写的 Node 替代品,用 JSC 引擎,主打快和全家桶,兼容 npm 生态,装依赖是它最无痛的入口。什么时候值得深入:你开始频繁写独立的 TS 脚本/小服务(Bun 的零配置体验是真香),或者你的团队被 CI 装依赖的速度逼疯了。
随堂测验
1. 「JS 引擎」和「JS 运行时」的关系,下列哪个说法正确?
2. 为什么浏览器里的 JS 不能像 Node 那样直接读写你电脑上的文件?
3. Bun 相比前辈 Deno,起步阶段少踩的最大的坑是什么?
4. 对你的 Next.js 项目而言,现阶段引入 Bun 风险最低、收益最直接的方式是?
划选正文任意文字可高亮、批注或加入复习卡
讨论
载入中…