Skip to content
扫码开始移动端阅读

从源码到可执行文件:Node.js 打包踩坑记录

1041
需要≈
5.205
分钟
JavaScript
杂谈

最近在开发一个app,后端部分我使用了nodejs,我最初并没有考虑打包的问题,直接在服务器上跑源代码。但当我把项目打包成 Docker 镜像 部署时,才意识到 源代码是可以被提取的! 这让我开始思考如何让代码更加安全,避免直接暴露在容器里。

于是,我开始尝试各种 Node.js 打包方案 ,这一路经历了不少坑,最终找到了一个相对合适的解决方案。


1. Docker 部署带来的安全隐患

当时,我的项目是直接 把 Node.js 代码放进 Docker 运行的,构建命令类似这样:

dockerfile
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

但后来我发现, Docker 镜像里的文件是可以被解包的

bash
docker save my-app:latest -o my-app.tar
tar -xvf my-app.tar

这意味着 源代码是可以被恢复的 !这对闭源项目来说,风险很大。

所以,我想到了 打包成二进制文件 ,让代码不会直接暴露。


2. ncc 打包:轻量但不支持二进制模块

最早,我使用了 ncc (@vercel/ncc)来进行打包:

bash
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 打包工具:

bash
npx pkg server.js --output app

它能打包成独立的可执行文件,听起来很完美,但实际遇到了几个大问题:

  1. 打包速度极慢 ,尤其是带 sharp 这种原生依赖的项目。
  2. 不支持 Node.js 20+ ,而我的项目要求 Node.js 20 以上
  3. 官网显示 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 运行时封装到一个可执行文件中:

json
{
  "main": "server.js",
  "output": "app"
}

然后打包:

bash
node --experimental-sea-config sea-config.json

执行速度非常快! 而且是官方支持的方案,未来可能会成为标准。

最大的问题 是:

  1. 文档没有说明如何处理二进制依赖 (如 sharpffmpeg)。
  2. 很多原生模块不兼容 ,项目执行时报错。

最终,我发现 SEA 还不够成熟,不适合当前的生产环境。


5. 解决方案:降级 sharp

回到 ncc,我继续研究 sharp 无法打包的问题 ,最终在 GitHub 上找到了解决方案:

最终,降级 sharp 后,ncc 成功打包并运行了项目 🎉


6. 结论:如何选择?

方案适用场景优势劣势
nccServerless、云函数体积小,动态 import 友好不支持二进制模块
Node.js SEA独立可执行文件,未来标准官方支持,运行快仍在实验阶段,二进制支持差

目前来看,如果你的应用需要 打包成独立可执行文件 ,可以 尝试 Node.js 21 SEA (但要注意二进制依赖问题)。如果是 Serverless 部署ncc 仍然是最佳选择 ,但要小心 sharp 版本问题。

最终,我的解决方案是 ncc + 降级 sharp ,这让我的项目顺利打包并运行!🚀

上次更新: