一个 100MB 的 CLI 二进制文件
Anthropic 刚刚宣布,Claude Code 已经可以作为 原生构建 使用了。翻译一下:这意味着你可以直接下载一个可执行的二进制文件(通过 curl 安装),不需要再依赖 Node.js。
听起来是不是很棒?一个命令,没有额外依赖,后台还能自动更新。简直就是每个 CLI 工具的梦想。
但是有一个问题:这个二进制文件竟然有 100MB 的大小。
作为对比,git 的二进制文件大约只有 3MB,而 curl 则不到 1MB。就算以生成较大二进制文件著称的 Go,它的文件一般也很少超过 15-20MB。
那么,这 100MB 到底装了些什么?
不是 Rust,而是披了可执行文件外衣的 Bun
当我看到这个公告时,第一反应是:“一定是用 Rust 重写了。” 毕竟,要实现快速的原生二进制文件,而且不依赖运行时,Rust 是明显的选择。
但事实并非如此。
Claude Code 仍然是 TypeScript。Anthropic 使用了 bun build --compile 命令,将其打包成了可执行文件。
bun build --compile 的工作原理
神奇的命令是这样的:
bun build ./src/index.ts --compile --outfile claude
这个命令到底做了什么?分为三步来解释:
1. 打包
首先,Bun 作为一个打包工具,处理你的入口文件(比如 index.ts),解析所有的 import,并生成一个包含所有代码的单一 JavaScript 文件。在这个过程中,还会自动引入 node_modules 里的依赖,进行 tree-shaking 删除无用代码,并对结果进行压缩优化。
到这一步,其实和 esbuild 或 webpack 做的事情没什么不同。
2. 嵌入
有趣的部分来了。Bun 将生成的 JavaScript 打包文件 嵌入到一个可执行文件中,同时将整个 Bun 的运行时也一并嵌入。
最终生成的可执行文件结构如下(简化版):
┌─────────────────────────────────┐
│ Bun 运行时 (~95MB) │
│ ├── JavaScriptCore 引擎 │
│ ├── 原生 API (fs, http) │
│ ├── Zig 运行时 │
│ └── 静态 libc │
├─────────────────────────────────┤
│ 你的代码打包 (~5MB) │
│ └── JavaScript 压缩代码 │
└─────────────────────────────────┘
运行这个二进制文件时,Bun 的运行时会解压自身并执行其中的 JavaScript 代码。有点类似一个自解压的 ZIP 文件,不过是用于代码的。
3. 交叉编译
Bun 还可以为其他平台生成二进制文件:
bun build ./src/index.ts --compile --target=bun-linux-x64 --outfile claude-linux
bun build ./src/index.ts --compile --target=bun-darwin-arm64 --outfile claude-mac
bun build ./src/index.ts --compile --target=bun-windows-x64 --outfile claude.exe
你不需要在 Linux 系统上生成 Linux 的二进制文件。Bun 已经内置了每个平台预编译的运行时。
JavaScriptCore vs V8
Node.js 使用的是 Chrome 的 JavaScript 引擎 V8,而 Bun 使用的是 WebKit/Safari 的 JavaScriptCore (JSC)。
为什么这很重要?因为这两者差别很大:
| 对比项 | V8 (Node.js) | JavaScriptCore (Bun) |
|---|---|---|
| 启动时间 | ~50 毫秒 | ~5 毫秒 |
| 峰值性能 | 非常高 | 高 |
| 内存使用率 | 较高 | 较低 |
| JIT 层级 | 2 (Ignition → TurboFan) | 4 (LLInt → Baseline → DFG → FTL) |
JSC 的启动速度更快,因为它有更多级别的 JIT 编译。从非常快速的解释器 (LLInt) 开始,一边执行一边在后台优化。而 V8 在开始执行前需要进行更多的初始化工作。
对于一个启动、执行完任务后立刻退出的命令行工具来说,这 45 毫秒的启动时间差异是关键点。而对于运行了数小时的服务器来说,差异就无关紧要了。
为什么用 Zig(而不是 Rust 或 C++)
Bun 主要用 Zig 语言编写,只有与 JavaScriptCore 交互的部分使用了少量 C++。
Bun 的创建者 Jarred Sumner 选择 Zig 的原因包括以下几点:
与 C 的互操作性:Zig 可以直接调用 C 代码,无需额外的开销或 FFI(外部函数接口)。JavaScriptCore 是用 C++ 编写的,Zig 能直接与其链接。
无垃圾回收的内存控制:和 Rust 类似,但语法更简洁,而且没有繁琐的 borrow checker。
轻松的跨平台编译:Zig 能从任意平台编译出适合其他平台的二进制文件。你无需在 Linux 机上为 Linux 编译。
相对较小的二进制文件:用 Zig 编写的“Hello World”程序大约 5KB,而 Go 编写的约为 2MB,Rust 编写的大约 300KB。
最终,Bun 的运行时实现了快速启动、低内存使用,并且可以作为一个单独的静态二进制文件分发。这种特性非常适合这种应用场景。
它不是什么
需要明确的是:这 并不是像 GraalVM 对 Java 或者 WASM 的那种 AOT(预编译)。
你的 TypeScript 代码 并不会被编译为 CPU 指令。它仍然由 JavaScriptCore 在运行时解释执行。唯一的变化是解释器和代码被一起打包了起来。
这个方式和以下做法相似:
- Node.js 的
pkg工具 - Python 的
PyInstaller - 构建 Electron 应用的
electron-builder
这不是魔法。只是把运行时和代码放到一个单独的包里。所以这个二进制文件之所以会有 100MB 的大小,大部分原因在于 JavaScriptCore 和 Bun 的 API,而不是 Claude Code 的代码。
Anthropic 的收益是什么?
这是核心问题。因为这个改变根本不是为了你。
降低支持票数量
你知道 npm 会带来多少问题吗?权限问题、缓存损坏、Node 版本冲突、PATH 配置错误、神秘失踪的 800MB 的 node_modules…
每一个问题都可能导致一个用户提交技术支持请求。而每一个支持请求都意味着金钱和时间的投入。对于拥有数百万用户的 Claude Code 来说,减少与 npm 相关的问题是他们的主要动机。
无摩擦的自动更新
通过 npm,更新 Claude Code 的过程是:运行 npm update -g @anthropic/claude-code 命令。有的人会更新,但很多人不会。
而通过原生构建文件,更新可以在后台自动完成。Anthropic 对你运行的版本拥有完全的控制权。对于一个需要快速迭代并频繁修复错误的公司来说,这种控制是一笔巨大的财富。
可控的运行环境
当收到一个错误报告时,首先要问的是:“你用的是哪个版本的 Node?npm 版本是多少?操作系统是什么?”
使用原生构建后,这一切都消失了。每个人的执行环境完全一致。
可复现的错误 = 更容易解决的错误。
用户的收获是什么?
如果你没有安装 Node.js
显而易见的提升。之前你需要:
- 安装 Node.js
- 配置 PATH(如果没有自动完成)
- 安装 npm(尽管随 Node 一起安装,有时仍需更新)
- 运行
npm install -g @anthropic/claude-code - 祈祷不会出现版本冲突
现在你只需要:
curl -fsSL https://claude.ai/install.sh | sh
对于非开发者 —— 产品经理、写作爱好者或设计师 —— 这个简化流程显然是巨大的差异。
如果你已经安装了 Node.js
坦白说,提升有限。你可能能避免一些偶尔出现的 npm 问题。自动更新非常方便。此外,原生构建版提供的原生语法高亮功能也是一个锦上添花的小优点。
但如果你已经通过 npm 安装并正常使用 Claude Code,这次的变化基本上对你的日常使用影响不大。
如何看待 100MB 的二进制大小
100MB 听起来或许很夸张。一个 CLI 工具而已。我们的前辈们用 4KB 内存编程,如今我们却要下载 100MB 的文件。
然而,实际来看:
- VS Code ~300MB
- Slack ~500MB
- iOS SDK ~30GB
- 一个典型的 Node.js 项目,其
node_modules文件夹大小接近 500MB+
还有我的最爱:macOS Sequoia 包含 45GB 的壁纸。
四十五个 GB。全是壁纸。4K 下观赏的水母浮动、海浪拍岸、极光闪动,仅仅是为了让你的 iMac 有个漂亮的屏保。而这达到 Claude Code 体积的 450 倍。
Claude Code 可以成为一个能编写代码、执行命令和管理整个项目的 AI 助手。而苹果的壁纸带给你的,只是一些水母。
到 2026 年,当 1Gbps 的光纤和 1TB 的 SSD 成为标准配置时,100MB 根本就是可以忽略的体量。下载只需要几秒钟,保存下来后很快就会忘记它的存在。
是不是优雅的解决方案?不是。但在实践中,它真的重要吗?显然不太重要。甚至可以说,比那些水母更不重要。
你是否应该迁移?
如果你已经有可用的 npm 版本,那并不需要着急迁移。你可以通过现有的 npm 版本直接运行 claude install 来切换到原生构建版本。
如果你是第一次安装 Claude Code,请使用原生构建版本。这是官方推荐的选择,并且能减少潜在使用问题的概率。
如果你非常在意一个 100MB 的可执行文件占用你的磁盘空间,你也可以继续使用 npm。Anthropic 表示会同时保留两种安装方式,但显然原生构建是未来的方向。
不断重复的模式
其实这并不新鲜。这种模式反复在软件开发中上演:
- 起初你会依赖共享的依赖(DLL、系统库、npm 包)
- 然而,你面临多个版本和分发上的兼容性问题
- 最后你选择把一切都打包到一个“简单可用”的庞大包中
Docker 就是这样,Electron 也是这样,Bun 同样也是这样。
这是最优雅的解决方案吗?不是。但它几乎总是问题最少的。
有时候,工程学的目标不是找到最美的解决方案,而是那个能产生最少技术支持工单的方案。而年利润数十亿、用户数以百万计的 Anthropic 深谙其道。
相关文章:
- Bun: 想取代 Node 的运行时 — 面向来自 Node 开发者的 Bun 完整教程,包括 Anthropic 的收购。
- 10GB VM 支持一个聊天机器人 — 为什么 Claude Desktop 在 Mac 上嵌入整个 Ubuntu 系统:又一次功能胜于优雅的决策。
本文原文为西班牙语,借助AI翻译。