调用一个运行会阻塞的DLL,想到用子进程的方式启动,这样Electron不会阻塞,之间也能方便通信
想到用Fork函数来实现,新建两个脚本,main.js与child.js
main.js
const { fork } = require('child_process');
const child = fork('./child.js');
child.send({ "now": "start" });
child.on('message', (message) => {
console.log(`Fork process say: ${message.msg} ${message.data}`);
});
child.js
process.on('message', (message) => {
process.send({ msg: "hello~", data: "something" });
});
用Node直接运行,通信良好,也达到了想要相互通信的预期,于是把代码放到了Electron中运行,也没有问题,但是坑同时埋下了
(一)问题的产生
直到使用electron-builder打包后,问题才暴露出来,DLL提供的功能没有生效,测试发现使用fork()函数执行的脚本没有被,检查了一遍路径,同时也打了不少日志,搜索了一圈,发现确实是fork()导致的问题
fork()函数相当于spawn()的子集
const child = fork('./child.js');
const child = spawn(process.execPath, ['./child']);
这两行作用是相同的,如果在spawn中想要使用像fork中child.send()
方式来通信,需要再修改点参数
const child = spawn(process.execPath, ['./child'], {
stdio: [0, 1, 2, 'ipc']
});
这样它们的行为和代码就一致了。这里的process.execPath
是指运行主进程的可执行程序,我用node直接运行的脚本,所以这个值为C:\Program Files (x86)\nodejs\node.exe
,所以fork的默认行为即为用运行主进程的程序运行指定的脚本,问题就处在这里了。
在Electron的项目中,process.execPath
值为项目目录下的node_modules\electron\dist\electron.exe
,其实`node_modules\electron\dist
下的所有文件,相当于一个node的独立编译版了~ 这样,开发的时候,因为一直都是在终端electron .
方式启动的程序,问题没有暴露,打包后,这个process.execPath
值变化了!
打包后,它的值变为了dist-dir\productName.exe
,也就是目录里的可执行程序
electron.exe有运行脚本的功能,但是打包后的应用exe却没有执行外部脚本的能力,所以打包后使用fork执行的代码没有被执行
这里备注一下:上边fork与spawn行为还有些不一样的地方,使用fork的代码打包后,fork引入的代码没有被执行,如果使用spawn指定process.execPath后,启动应用后它会一遍一遍的启动自身
spawn的行为很好理解,fork可能没有使用process.execPath
(二)解决问题的办法
既然fork和spawn没那么神秘了,他们都是通过调用可执行程序来解析需要执行的脚本
那在不修改调用方式的前提下,有一些解决办法,不过都很不优雅
1)安装electron程序的机器首先安装个node,并且安装子进程所需要的依赖
2)把electron的dist目录拷贝一份,用electron-builder的extraFiles参数把它打包,但是很丑陋,electron程序里打包了两个electron...
3)使用nexe包,把child.js打包成一个独立的exe可执行程序,然后spawn把它启动起来(可能会编译失败)
不过最后联调,没使用上边的任何方式,修改了dll,不会再阻塞住进程,在主进程直接调用,避免执行脚本,从而根本上避免了问题。
参考: