Node 打包工具
约 801 字大约 3 分钟
nodebundler
2026-04-08
目录:
前端打包工具关注的是浏览器产物,而 Node 端"打包"关注的是 如何把一个 Node 项目分发给没有 Node 环境的目标机器。常见诉求:
- 把 CLI 工具打成单文件,免安装即用
- 把服务发布到无 npm install 权限的机器
- 把 node_modules 摊平到一个 dist 里,加速冷启动
下面是最常用的两个工具。
pkg
pkg 由 Vercel 出品(现已进入 maintenance),它的核心能力是把 Node 运行时和 JS 源码一起打成单个原生可执行文件。
特点:
- 产物可以直接
./app运行,无需安装 Node - 支持交叉编译:在 macOS 上一次构建出
linux-x64/win-x64/macos-arm64三份产物 - 把脚本、资源文件、原生模块一并嵌入
基本用法:
pnpm add -D pkg
# package.json
{
"bin": "dist/index.js",
"pkg": {
"targets": ["node18-linux-x64", "node18-macos-arm64", "node18-win-x64"],
"assets": ["templates/**/*"],
"outputPath": "release"
}
}
pnpm pkg .适用场景:
- 分发给非技术用户的桌面 CLI
- 内网部署、目标机器没有 Node
- 需要把代码闭源 / 防止源码泄漏(pkg 把 JS 编译为 V8 字节码)
注意事项:
- 动态
require路径需要在pkg.assets里显式声明,否则会被剔除 - 内含原生模块(如
better-sqlite3)需匹配目标平台 - 产物体积较大(包含完整 Node 二进制,~40MB+)
@vercel/ncc
@vercel/ncc 是 GitHub Actions 同款的 Node 打包器,目标是 把入口文件 + 所有依赖打成一个 index.js,不嵌入 Node 运行时。
特点:
- 输出是一个纯 JS 文件,仍需要目标机器有 Node
- 体积远小于 pkg(不含 Node 二进制)
- 自动 tree-shaking、处理
__dirname/ 动态 require / 原生模块 - 默认产物自带 sourcemap,便于线上排错
基本用法:
pnpm add -D @vercel/ncc
ncc build src/index.ts -o dist
# 产出 dist/index.js(一个文件,包含全部依赖)适用场景:
- GitHub Action:仓库里只能提交
dist/index.js,靠 ncc 打包替代npm install - Serverless / FaaS:冷启动时间敏感,单文件加载远快于解析整棵 node_modules
- 内部 CLI 分发,但目标机器有 Node
详细实战可参考站内文章:/article/axky6ki1/
pkg vs ncc 选型
| 维度 | pkg | @vercel/ncc |
|---|---|---|
| 是否嵌入 Node 运行时 | ✅ 是 | ❌ 否 |
| 产物形态 | 单个原生可执行文件 | 单个 .js 文件 |
| 产物体积 | 大(~40MB+) | 小(仅业务代码 + 依赖) |
| 跨平台 | 支持交叉编译 | 与 Node 版本无关 |
| 典型场景 | CLI 分发给非技术用户 | GitHub Action / Serverless / 内部部署 |
| 维护状态 | 进入维护模式 | 活跃 |
经验法则:目标机器上有 Node 就选 ncc,没有就选 pkg。
其他选项
- esbuild / rollup with
platform: 'node':用于库作者或想要更细控制的场景,本质和 ncc 类似但需要自己配 externals - bun build --target=node:Bun 自带的 Node 兼容打包器,速度极快
- nexe:和 pkg 思路一致的另一个选择,社区维护
小结
Node 端打包的核心问题永远是"目标机器有什么、缺什么"。想清楚这一点,再回头看工具列表,选型几乎是显而易见的。
