赞
踩
本文地址:https://superliii.blog.csdn.net/article/details/124114250 (拒绝搬运)
最近项目搞得差不多了,开始尝试打包Node后台程序。
关于这个问题我卡了两天,尝试了各种办法,期间也走了不少弯路。
相关方面资料太少,在此分享出一套方案实现。
帖子有些啰嗦,你可以直接下载我的工程案例瞅一下:案例工程
pkg用起来各种问题,不知道pkg是压根就不支持ES6还是我没搞明白,但它明明引用了babel!
好吧,我们自动手动搞一个babel ES6->ES5编译过程。
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node
npm install --save-dev pkg
{
"presets": [
"@babel/preset-env"
]
}
{ ... "type": "module", "bin": "dist/es5/index.js", "pkg": { "options": [ "experimental-modules" ], "scripts": "dist/es5/**/*.js", "targets": [ "node16-win-x64" ], "outputPath": "dist" }, "scripts": { ... "build": "babel src -d dist/es5 && pkg ." ... }, ... }
解释下,上面这些参数都是干啥的:
键 | 备注 |
---|---|
type | 告诉NodeJS我们的JS使用ES6的是语法。 |
bin | pkg要打包程序的入口文件 |
pkg | pkg的一堆参数配置 |
pkg->options | pkg要打包程序的入口文件 |
pkg->scripts | 一些动态引用无法被pkg捕捉到,我们需要告诉pkg这些东西也要一并打包出去。比如import(‘aaa.js’)这样的语法。 |
pkg->targets | 要构建的目标平台,有许多,比如node16-linux-arm64,请自行搜索 |
scripts->build | 这里是我们写的自动构建命令行,让babel完事儿后自动pkg |
所以以上就实现了pkg构建es6项目。
但事情还没有结束,我们还需要对fork做处理。
按照我们以往node的开发习惯,直接fork另一个路径的js就搞定了,但是在pkg中有所不同:
假设我们有index.js和child.js两个文件,经过pkg打包后你会发现干净到只有一个exe可执行文件。
所以我们直接fork(‘child.js’) pkg是找不到child.js这个文件的,除非你把他复制了过去。
用notepad++打开发布后的exe文件看了下,pkg就简单粗暴的把代都合并在里面,并没有加密混淆。
所以如果我们需要保护代码,需要在babel后用Uglifyjs混淆一下。
如果需要压缩,pkg提供了压缩指令,我们只需修改打包指令为pkg . -C Gzip
关于压缩算法pkg还提供了Brotli算法,对比戳这
无意中发现,pgk打包后的process.argv[1]输出为C:\snapshot\…
这个东西就很奇怪,看了一下C盘下并没有snapshot这个目录,于是猜想它应该是个某种虚拟目录。
翻箱倒柜,作者在github上给出了关于snapshot快照部分的介绍
链接: https://github.com/vercel/pkg#snapshot-filesystem。
在此,我搬来翻译一下(为便于理解,有所修改):
快照文件系统(Snapshot filesystem)
在打包过程中,收集项目文件并将其放入可执行文件中,它称为快照。
在运行时,打包的应用程序可以访问所有文件所在的快照文件系统。
.
如果你需要使用路径,则以下取值你很可能会用到。
value 值 with(node)打包前 packaged 打包后 comments 备注 __filename /project/app.js /snapshot/project/app.js 打包后为虚拟快照路径 __dirname /project /snapshot/project 打包后为虚拟快照路径 process.cwd() /project /project 真实的运行路径 process.execPath /usr/bin/nodejs /deploy/app-x64 运行程序路径 process.argv[0] /usr/bin/nodejs /deploy/app-x64 命令行参数0 (执行程序) process.argv[1] /project/app.js /snapshot/project/app.js 命令行参数1(一般是指.js文件) process.pkg.entrypoint undefined /snapshot/project/app.js 入口文件路径 process.pkg.defaultEntrypoint undefined /snapshot/project/app.js 默认入口文件路径 require.main.filename /project/app.js /snapshot/project/app.js 入口文件路径 注意:在 Windows 中,路径会具有盘符前缀。
因此,为了使用在打包时收集的文件(js文件或任何资产),您应该采用 __dirname、 或 __filename作为路径计算的基础。对于javascript文件,您可以只是或因为它们默认使用当前。对于资产,请使用 .有关详细信息,请参阅检测源代码中的资产。
.
另一方面,为了在运行时访问真实的文件系统,你应该使用process.cwd()或path.dirname(process.execPath)。
在windows上实测效果如下:
value 值 | with(node)打包前 |
---|---|
__filename | C:\snapshot\test\dist\es5\index.js |
__dirname | C:\snapshot\test\dist\es5 |
process.cwd() | F:\****\test\dist |
process.execPath | F:\****\test\dist\test.exe |
process.argv[0] | F:\****\test\dist\test.exe |
process.argv[1] | C:\snapshot\test\dist\es5\index.js |
process.pkg.entrypoint | C:\snapshot\test\dist\es5\index.js |
process.pkg.defaultEntrypoint | C:\snapshot\test\dist\es5\index.js |
require.main.filename | C:\snapshot\test\dist\es5\index.js |
所以,我们应该在fork里,使用绝对路径来替代相对路径。
cprocess.fork(__dirname + "/child.js", {
env: { a: "aaa" },
});
这样发布后欠确实是好用了
在ES6中,Node没有提供__dirname、__filename。
所以现在的情况是:直接node运行会报__dirname undefind错误。
查了下node文档,上面告诉我们可以使用import.meta.url替代。
但是它无法被babel转为es5。pkg后依旧会报错,且这个错误try捕捉不到。大崩直接退进程!
JS不像C++有宏编译,不能根据运行环境编译不同代码。
要么我们在外部配置文件指定__dirname,要么我们干脆就抛弃它。
于是…
你也许会考虑fork自身入口文件,通过不同的命令行参数来执行不同的任务,就像Chrome那样。
事实上你是对的,这样确实行得通。我们来尝试一下:
创建一个src目录,随后在搞两个测试文件index.js和child.js
//index.js
import cprocess from "child_process";
import child from "./child.js";
if (process.env.a == null) {
console.log("LOL!!", process.argv0, process.argv[1]);
cprocess.fork(process.argv[1], {
env: { a: "aaa" },
});
} else {
child();
}
//child.js
export default function () {
console.log("IM CHILD!!");
}
案例工程下载:https://download.csdn.net/download/cbaili/85113925
运行一下,成功了!
这套机制使用import的方式,成功的规避掉了ES6 __dirname找不到的问题。
index.js是入口文件,主要负责不同进程的fork操作。
通过不同的env参数,区分不同的fork操作。
完结撒花,这是一套实在的解决方案,拿来分享给大家。
觉得有用记得点赞收藏哦~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。