从源码到可执行文件:Node.js 打包踩坑记录
最近在开发一个app,后端部分我使用了nodejs,我最初并没有考虑打包的问题,直接在服务器上跑源代码。但当我把项目打包成 Docker 镜像 部署时,才意识到 源代码是可以被提取的! 这让我开始思考如何让代码更加安全,避免直接暴露在容器里。
于是,我开始尝试各种 Node.js 打包方案 ,这一路经历了不少坑,最终找到了一个相对合适的解决方案。
1. Docker 部署带来的安全隐患
当时,我的项目是直接 把 Node.js 代码放进 Docker 运行的,构建命令类似这样:
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
但后来我发现, Docker 镜像里的文件是可以被解包的 :
docker save my-app:latest -o my-app.tar
tar -xvf my-app.tar
这意味着 源代码是可以被恢复的 !这对闭源项目来说,风险很大。
所以,我想到了 打包成二进制文件 ,让代码不会直接暴露。
2. ncc 打包:轻量但不支持二进制模块
最早,我使用了 ncc (@vercel/ncc)来进行打包:
npx @vercel/ncc build server.js -o dist
它会把所有 JS 代码打包成一个 .js
文件,并减少 node_modules
体积,非常适合 Serverless 部署 。
但 问题来了 ,我的项目用到了 sharp
,打包后执行时报错:
Error: Could not load the "sharp" module using the darwin-arm64 runtime
Possible solutions:
- Ensure optional dependencies can be installed:
npm install --include=optional sharp
- Ensure your package manager supports multi-platform installation:
See https://sharp.pixelplumbing.com/install#cross-platform
- Add platform-specific dependencies:
npm install --os=darwin --cpu=arm64 sharp
我一直以为是 ncc
不会处理二进制文件 ,于是尝试了各种方案,包括 手动拷贝 .node
文件 ,但都失败了。
3. pkg:放弃维护的过时方案
因为 ncc
不行,我转而尝试 pkg ,这是之前最流行的 Node.js 打包工具:
npx pkg server.js --output app
它能打包成独立的可执行文件,听起来很完美,但实际遇到了几个大问题:
- 打包速度极慢 ,尤其是带
sharp
这种原生依赖的项目。 - 不支持 Node.js 20+ ,而我的项目要求 Node.js 20 以上 。
- 官网显示 pkg 已被弃用 :
pkg has been deprecated with 5.8.1 as the last release.
We’re excited about Node.js 21’s support for single executable applications.
当我去 pkg 的 GitHub 仓库 看了一下,发现它已经变成了 只读状态 ,官方推荐使用 Node.js 21 的 Single Executable Applications (SEA) 方案。
既然 pkg
已经凉了,我决定尝试 Node.js 21 的原生打包 。
4. Node.js 21 SEA:原生但文档不完善
Node.js 21 引入了 Single Executable Applications (SEA) ,它能把 JS 代码和 Node.js 运行时封装到一个可执行文件中:
{
"main": "server.js",
"output": "app"
}
然后打包:
node --experimental-sea-config sea-config.json
执行速度非常快! 而且是官方支持的方案,未来可能会成为标准。
但 最大的问题 是:
- 文档没有说明如何处理二进制依赖 (如
sharp
、ffmpeg
)。 - 很多原生模块不兼容 ,项目执行时报错。
最终,我发现 SEA 还不够成熟,不适合当前的生产环境。
5. 解决方案:降级 sharp
回到 ncc
,我继续研究 sharp 无法打包的问题 ,最终在 GitHub 上找到了解决方案:
sharp@0.32.5+
不兼容ncc
- 需要 降级到 0.32.5 才能正常打包
- 官方 issue 参考:https://github.com/vercel/ncc/issues/1153
最终,降级 sharp
后,ncc
成功打包并运行了项目 🎉
6. 结论:如何选择?
方案 | 适用场景 | 优势 | 劣势 |
---|---|---|---|
ncc | Serverless、云函数 | 体积小,动态 import 友好 | 不支持二进制模块 |
Node.js SEA | 独立可执行文件,未来标准 | 官方支持,运行快 | 仍在实验阶段,二进制支持差 |
目前来看,如果你的应用需要 打包成独立可执行文件 ,可以 尝试 Node.js 21 SEA (但要注意二进制依赖问题)。如果是 Serverless 部署 , ncc 仍然是最佳选择 ,但要小心 sharp
版本问题。
最终,我的解决方案是 ncc + 降级 sharp ,这让我的项目顺利打包并运行!🚀