Node.js v21.7.1 支持将 JavaScript 代码 “打包” 为单个可执行文件了,那么在此之前怎么把自己应用程序打包发布给别人呢?
我:你先去 https://nodejs.org/en 下载安装一个 nodejs,然后下载我这 app.js,最后打开你的终端或 CMD 执行 node app.js
那当然是直接 Electron 一把嗦啦,实在不行先拷贝一个 nodejs 二进制文件,然后写一个 bash 或者 bat 启动脚本又不是不能用对吧,好了博客到此结束( 🐶。
还是不扯了,让我们一起来看看 Node.js v21.7.1 实验性特性打包为单个可执行文件的流程吧。
首先得通过 sea-config 将 app.js(代码文件)和所需要的 assets 静态资源编译为单个 blob 二进制文件,sea-config 的配置目前也比较简单,在项目根目录创建一个 sea-config.json 文件,配置选项可以参考官方文档。
{
"main": "dist/index.js",
"output": "dist/out.blob",
"disableExperimentalSEAWarning": true, // Default: false
"useSnapshot": false, // Default: false
"useCodeCache": true, // Default: false
"assets": { // Optional
"a.dat": "/path/to/a.dat",
"b.txt": "/path/to/b.txt"
}
}
配置好之后,执行 node --experimental-sea-config sea-config.json
即可生成 dist/out.blob 文件。
打包好 blob 文件后,执行 cp $(command -v node) app
拷贝一份 node 可执行文件到输出目录,是的你没看错就是拷贝一份 node 可执行文件。
最后将 dist/out.blob 文件注入到 app 可执行文件中就可以了,这里我的运行环境为 macos,如果是 linux 或者 windows 的话可以参考官方文档
npx postject app NODE_SEA_BLOB dist/out.blob \
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
--macho-segment-name NODE_SEA
如此,app 就打包成功了,但是这 helloworld 打包后的文件大小 93M 我不太好评价,不过想想也是蛮合理的比较其实就是 nodejs + 代码和静态文件大小,整个打包过程就是将代码文件注入到 nodejs 的可执行文件的过程,期待后面能使用其他 JS 运行时的集成,未来可期。
我分享一下自己的 package.json 打包配置,也许有小伙伴配置打包时可以参考一下
{
"name": "app",
"version": "1.0.0",
"description": "Single executable applications",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "npm run build:ncc && npm run build:sea && npm run build:copy && npm run build:injection",
"build:ncc": "ncc build index.ts -m -o dist",
"build:sea": "node --experimental-sea-config sea-config.json",
"build:copy": "node -e \"require('fs').copyFileSync(process.execPath, require('./package.json').name)\"",
"build:injection": "npx postject $(node -e \"console.log(require('./package.json').name)\") NODE_SEA_BLOB ./dist/index.blob ^ --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 ^ --macho-segment-name NODE_SEA",
"build:remove-signature": "codesign --remove-signature $(node -e \"console.log(require('./package.json').name)\")"
},
"author": "PBK-B",
"license": "ISC",
"pkg": {
"targets": [
"macos"
]
},
"devDependencies": {
"@types/node": "^20.11.27",
"@vercel/ncc": "^0.38.1",
"postject": "^1.0.0-alpha.6",
"typescript": "^5.4.2"
}
}