参考vite-plugin-electron插件作者的文章
Electron 是如何启动的
Electron 官网上有个关于启动的教程是需要配置 npm-scripts 里面一个命令electron .
{
"scripts": {
"start": "electron ."
}
}
{
"scripts": {
"start": "electron ."
}
}
这个命令的工作原理是调用 node_modules/electron/cli.js 文件来启动electron,它是一个 Node.js 脚本
#!/usr/bin/env node
// electron 可执行文件的路径
const electron = require('./');
// require('./') 即 require('electron')
// 这里的 electron 是一个可执行的二进制文件
// 在 Windows 系统中名字叫 `electron.exe`
// 在类 Unix 系统中名字叫 `electron`
const proc = require('child_process');
// 通过 Node.js 提供的 `spawn` 拉起 electron 进程
// 当调用 proc.spawn() 方法时,可以传递几个参数来控制子进程的创建和运行方式。下面是对这些参数的详细解释:
// electron: 这是一个变量,存储了 electron 可执行文件的路径。作为子进程要运行的应用程序。
// process.argv.slice(2): 这是一个数组,它包含了执行脚本时传递的命令行参数。process.argv 是一个包含命令行参数的数组,其中第一个元素是 Node.js 可执行文件的路径,第二个元素是脚本文件的路径,其余的元素是传递给脚本的命令行参数。slice(2) 是为了去除前两个元素(Node.js 和脚本文件的路径),只保留脚本的命令行参数。
// stdio: 'inherit': 这表示将子进程的标准输入、输出和错误输出直接连接到父进程,使得子进程的输出可以在父进程的控制台显示。也就是说,父进程和子进程共享相同的输入输出流,这样子进程的输出将直接打印到父进程的控制台上,使得它们的输出可以同时显示在一起。
// windowsHide: false: 这个选项在 Windows 系统上特别有用。它的值是一个布尔型。当设置为 false 时,子进程在 Windows 上创建时不会隐藏窗口,即子进程会在界面上显示出来。如果设置为 true,子进程会在后台运行,没有窗口显示。
// 所以,proc.spawn() 方法的调用是为了创建一个子进程,子进程将运行 electron 应用程序,并且传递给 electron 的命令行参数来自父进程执行此脚本时传递的命令行参数。同时,子进程的标准输入输出和错误输出与父进程共享,并且在 Windows 上不会隐藏子进程的窗口。这样做可以使得 electron 应用程序的输出显示在父进程(即此脚本)的控制台上,方便调试和查看输出信息。
const child = proc.spawn(electron, process.argv.slice(2), { stdio: 'inherit', windowsHide: false });
// 这等价于我们用鼠标双击了 Electron App 的图标启动了它
// 所以说这一点都不神奇,我们也可以复刻同样的操作 ---- 在插件中也是这个实现方式
// 这段代码的作用是在子进程关闭时,检查退出码。如果退出码为 null,则说明子进程是通过信号终止的,会输出一个错误信息并使得脚本以非零退出码退出。如果退出码是正常的,脚本会以子进程的退出码退出,以便让父进程了解子进程的执行结果
child.on('close', function (code, signal) {
// 检查子进程的退出码(code)。如果退出码为 null,这意味着子进程是通过信号终止的而不是通过正常的退出码。例如手动使用 Ctrl+C 终止子进程。
if (code === null) {
console.error(electron, 'exited with signal', signal);
// 如果子进程是通过信号终止的,则执行此行代码,意味着脚本将以非零退出码(1)退出。这表示脚本的执行出现了错误或异常情况
process.exit(1);
}
// 如果子进程是通过正常的退出码终止的,那么执行此行代码,脚本将以与子进程相同的退出码退出。这样可以让父进程知道子进程的执行状态,通常约定 0 表示成功,非零表示出现错误
process.exit(code);
});
// 在接收到 SIGINT 和 SIGTERM 终止信号时,优雅地终止子进程。它检查子进程是否已经被终止,如果子进程还在运行,会向子进程发送相应的终止信号,以便子进程有机会做一些清理工作,然后正常退出。这样做可以避免子进程的突然终止,有助于保持应用程序的稳定性和数据完整性。
const handleTerminationSignal = function (signal) {
process.on(signal, function signalHandler () {
if (!child.killed) {
child.kill(signal);
}
});
};
handleTerminationSignal('SIGINT');
handleTerminationSignal('SIGTERM');
#!/usr/bin/env node
// electron 可执行文件的路径
const electron = require('./');
// require('./') 即 require('electron')
// 这里的 electron 是一个可执行的二进制文件
// 在 Windows 系统中名字叫 `electron.exe`
// 在类 Unix 系统中名字叫 `electron`
const proc = require('child_process');
// 通过 Node.js 提供的 `spawn` 拉起 electron 进程
// 当调用 proc.spawn() 方法时,可以传递几个参数来控制子进程的创建和运行方式。下面是对这些参数的详细解释:
// electron: 这是一个变量,存储了 electron 可执行文件的路径。作为子进程要运行的应用程序。
// process.argv.slice(2): 这是一个数组,它包含了执行脚本时传递的命令行参数。process.argv 是一个包含命令行参数的数组,其中第一个元素是 Node.js 可执行文件的路径,第二个元素是脚本文件的路径,其余的元素是传递给脚本的命令行参数。slice(2) 是为了去除前两个元素(Node.js 和脚本文件的路径),只保留脚本的命令行参数。
// stdio: 'inherit': 这表示将子进程的标准输入、输出和错误输出直接连接到父进程,使得子进程的输出可以在父进程的控制台显示。也就是说,父进程和子进程共享相同的输入输出流,这样子进程的输出将直接打印到父进程的控制台上,使得它们的输出可以同时显示在一起。
// windowsHide: false: 这个选项在 Windows 系统上特别有用。它的值是一个布尔型。当设置为 false 时,子进程在 Windows 上创建时不会隐藏窗口,即子进程会在界面上显示出来。如果设置为 true,子进程会在后台运行,没有窗口显示。
// 所以,proc.spawn() 方法的调用是为了创建一个子进程,子进程将运行 electron 应用程序,并且传递给 electron 的命令行参数来自父进程执行此脚本时传递的命令行参数。同时,子进程的标准输入输出和错误输出与父进程共享,并且在 Windows 上不会隐藏子进程的窗口。这样做可以使得 electron 应用程序的输出显示在父进程(即此脚本)的控制台上,方便调试和查看输出信息。
const child = proc.spawn(electron, process.argv.slice(2), { stdio: 'inherit', windowsHide: false });
// 这等价于我们用鼠标双击了 Electron App 的图标启动了它
// 所以说这一点都不神奇,我们也可以复刻同样的操作 ---- 在插件中也是这个实现方式
// 这段代码的作用是在子进程关闭时,检查退出码。如果退出码为 null,则说明子进程是通过信号终止的,会输出一个错误信息并使得脚本以非零退出码退出。如果退出码是正常的,脚本会以子进程的退出码退出,以便让父进程了解子进程的执行结果
child.on('close', function (code, signal) {
// 检查子进程的退出码(code)。如果退出码为 null,这意味着子进程是通过信号终止的而不是通过正常的退出码。例如手动使用 Ctrl+C 终止子进程。
if (code === null) {
console.error(electron, 'exited with signal', signal);
// 如果子进程是通过信号终止的,则执行此行代码,意味着脚本将以非零退出码(1)退出。这表示脚本的执行出现了错误或异常情况
process.exit(1);
}
// 如果子进程是通过正常的退出码终止的,那么执行此行代码,脚本将以与子进程相同的退出码退出。这样可以让父进程知道子进程的执行状态,通常约定 0 表示成功,非零表示出现错误
process.exit(code);
});
// 在接收到 SIGINT 和 SIGTERM 终止信号时,优雅地终止子进程。它检查子进程是否已经被终止,如果子进程还在运行,会向子进程发送相应的终止信号,以便子进程有机会做一些清理工作,然后正常退出。这样做可以避免子进程的突然终止,有助于保持应用程序的稳定性和数据完整性。
const handleTerminationSignal = function (signal) {
process.on(signal, function signalHandler () {
if (!child.killed) {
child.kill(signal);
}
});
};
handleTerminationSignal('SIGINT');
handleTerminationSignal('SIGTERM');
信号终止和通过正常的退出码终止是什么意思
当一个进程终止时,有两种常见的方式:通过信号终止和通过正常的退出码终止。这两种方式可以用来表示进程是以何种方式结束的,通常用于传达进程的执行状态。
1. 信号终止: 在类Unix系统(例如Linux、macOS等)中,一个进程可以收到不同类型的信号。这些信号可以来自操作系统、其他进程,或者由用户发送。某些信号可以用来终止进程,例如SIGINT(通常由Ctrl+C发送)和SIGTERM(常用于优雅地终止进程)。当进程收到这样的信号时,它会被迫终止,无法继续执行。在这种情况下,进程的退出码被设为`null`。
2. 正常的退出码: 正常的退出码是进程成功终止时返回的代码。通常,0被视为成功的退出码,表示进程执行成功完成。而非零的退出码表示进程执行出现了某种错误或异常情况。在类Unix系统中,退出码范围一般是0到255之间。不同的退出码可以用来表示不同的错误类型或状态。
回到之前的代码片段中,它检查子进程的退出码是否为null
。如果是null
,意味着子进程是通过信号终止的,可能是用户手动终止了进程(如按下了Ctrl+C)。在这种情况下,脚本输出一个错误消息,并以非零退出码(1)退出,表示脚本执行出现了错误。如果子进程的退出码不为null
,则表明进程是通过正常的退出码终止的,脚本将以子进程的退出码退出,以便让父进程知道子进程的执行状态。
SIGTERM终止信号介绍
SIGTERM是一种常见的终止信号,通常用于优雅地请求进程终止。当系统或用户希望终止一个进程时,会发送SIGTERM信号,该信号告诉进程它应该进行清理操作并正常退出。
以下是SIGTERM通常出现的一些情况:
- 命令行终止:在类Unix系统上,用户通常可以在终端中按下Ctrl+C组合键,以发送SIGINT信号,这将请求终止当前运行的进程。在大多数情况下,操作系统会尝试在进程终止前先发送SIGTERM信号,以便进程可以进行善后操作。
- 系统关机:在操作系统即将关闭或重启时,系统会向所有正在运行的进程发送SIGTERM信号。这使得进程有机会在关闭前进行必要的清理和保存操作。
- 进程管理:进程管理工具(如Systemd或Supervisor)可以使用SIGTERM信号来请求服务或进程的终止。这允许进程有机会在终止前执行清理操作,并在必要时关闭文件或网络连接等资源。
- 容器管理:在容器化环境中,例如Docker,当用户通过命令或脚本请求停止容器时,通常会使用SIGTERM信号来请求容器内部的进程正常退出。
总之,SIGTERM是一种用于优雅地请求进程终止的信号。它允许进程有机会进行善后处理和资源清理,以确保进程在终止时能够正确地释放资源,避免数据损坏或资源泄漏。程序员可以在收到SIGTERM信号时编写相应的处理程序,以便进程可以在终止前进行必要的操作,然后正常退出。
正常的退出码
正常的退出码是进程在正常执行完成后返回给父进程的代码。在类Unix系统中,进程的退出码是一个8位整数,范围从0到255。通常,0表示成功完成,而非零值表示出现了某种错误或异常情况。不同的程序可以自定义退出码来表示不同的执行状态或错误类型。以下是一些常见的退出码和它们的含义:
- 0: 表示成功完成,没有错误。
- 1-127: 通常用于表示不同的错误情况,例如特定的程序错误、参数错误或文件未找到等。
- 128-255: 这些退出码通常用于表示进程终止的原因是接收到了一个信号。例如,128 + 9 表示进程收到SIGKILL信号,128 + 15 表示进程收到SIGTERM信号。
- 自定义退出码:某些程序会使用自定义的退出码,以表示更多特定的执行状态或错误类型。例如,某个程序可以定义退出码 200 表示特定的数据库连接错误,退出码 201 表示磁盘空间不足,等等。
需要注意的是,不同的程序可能对退出码的使用有不同的约定。通常情况下,0被视为成功,非零值被视为出现错误。如果你在编写程序或脚本时需要返回退出码,最好参考相关文档或约定,确保退出码的使用符合预期。
总之,正常的退出码是进程在成功执行或出现特定错误时返回给父进程的整数代码。它是一种用于表示进程执行状态的简单而有用的机制。