Node.js 针对不同平台实现electron的自动重载

前言

大前端目前发展处于高度自动化中, webpack-dev-server这样的工具提供了向HTML注入一段监听WebSocket的JavaScript代码来实现前端界面的热加载.
但是对于electron主进程electron-main来说, 实现这种代码修改立即重启进程的功能是比较难实现的.
但也不是完全没有办法, 今天, 我们使用Node.js标准模块fs来实现监听源码并自动重启electron进程.

使用 fs.watch()

fs.watch()函数监听系统文件事件, Linux下无法递归地监听一个目录, 共包括renamechange两种事件, 一次文件读写过程会触发多次change事件.

fs.watch(dir, { recursive: false }, (event, filename) => {
      // ...根据事件类型和文件名判断是否应该采取何种措施
});

Windows平台下强制关闭进程: taskkill

Linux下可以向进程调用kill()函数发送信号, 或者使用pkill -ef electron这样的命令来完成, 而Windows下只能使用taskkill命令:

> taskkill /IM electron.exe /F

其中IM代表"Image", 映像名称.

源码: auto-reload.js

/**
 * 针对不同平台实现自动重载.
 * 在Windows平台下通过使用taskkill /IM electron.exe /F命令强制关闭electron进程.
 */
const fs = require('fs');
const child_process = require('child_process');
const _ = require('lodash');

const rules = [
    { event: 'change', test: /^.*\.js$/ },
];

/**
 * 监听源码文件事件
 * @param {String} dir 要监听的目录, 子目录下的文件不会被监听
 * @param {Array} rules 规则数组, 每个规则是包含了"事件名"字符串和"文件名"正则表达式两个字段的对象: { event, test }
 */
function watch_source_code_file(dir, rules) {
    // 递归地监视仅在Windows和OSX系统上支持。 这就意味着在其它系统上要使用替代方案。
    fs.watch(dir, { recursive: true }, (event, filename) => {
        rules.forEach(it => {
            if (event === it.event && it.test.test(filename)) {
                _handler(event, filename); // 如果源码发生了变化, 则执行_handle()函数的相关逻辑
            }
        });
    });

    // 一次"保存文件"的操作可能触发多次"change"事件, 因此使用Lodash提供的函数去抖动功能
    const _handler = globalThis._handler || (globalThis._handler = _.debounce(handler, 800, { leading: true, trailing: false, }));
}

/** 编码electron主程序相关文件变化应该执行的操作 */
function handler(event, filename) {
    reload_electron();
}

/** 重启electron主程序, 请调用者捕获异常 */
function reload_electron() {
    try {
        kill_electron();
    } catch (error) {
        console.error(error);
        console.log('未能成功关闭electron进程, 或electron进程不存在');
    }
    start_electron();
}

/** 不同平台下关闭electron进程的实现 */
function kill_electron() {
    if (process.platform.match(/^win.*/)) { // Implement this on Windows OS
        // child_process.execSync(`taskkill /PID electron.exe /F`);
        child_process.execSync(`taskkill /IM electron.exe /F`);
    } else if (process.platform.match(/^linux.*/)) { // Implement this on Linux OS
        globalThis.electron && globalThis.electron.kill();
    }
}

/** 不同平台下启动electron进程的实现 */
function start_electron() {
    if (process.platform.match(/^win.*/)) { // Implement this on Windows OS
        const cmd = `start cmd /c electron "${__dirname}/main.js"`;
        child_process.exec(cmd);
    } else if (process.platform.match(/^linux.*/)) { // Implement this on Linux OS
        const cmd = `bash -c 'electron "${__dirname}/main.js"'`; // 这里或许需要使用exec命令替换父进程, 以直接获取electron进程
        globalThis.electron = child_process.exec(cmd); // Linux下可以记录PID以kill进程, 当然也可以使用"pkill -f electron"命令
    }
}

/** 主程序 */
function main() {
    watch_source_code_file(__dirname, rules); // 监听项目根目录, 不含子目录, 规则在rules中定义
    start_electron();
}

main();
posted @ 2020-09-02 14:32  develon  阅读(1239)  评论(0编辑  收藏  举报