Electron中使用fork()函数的坑

Published: 2018-08-20

Tags: Electron

本文总阅读量

调用一个运行会阻塞的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,不会再阻塞住进程,在主进程直接调用,避免执行脚本,从而根本上避免了问题。

参考:

  1. Allow to childProcess.fork() Electron from Electron