有什么方法可以让我在 electronjs 中 运行 child_process 并发送 json 文件来对 child_process 创建的文件做出反应?

Is there any way that I can run child_process in electronjs and send json file to react that is being created by that child_process?

  1. 我有一个使用 nodejs 构建的 CLI 应用程序,我可以传递一些标志,它会输出到 JSON 文件。现在我想让它成为一个桌面应用程序,它将 JSON 文件数据显示为 UI。我正在尝试将 reactjs 用于 UI。此外,必须存储一些将用于 运行 CLI 的信息。

  2. 我尝试使用 child_process (exec(), spawn()) 将 CLI 应用程序集成到 electronjs,但它不起作用,我也不知道为什么。此外,我尝试使用将生成 CLI 应用程序 child_process 并使用 JSON 响应发送输出的 expressjs 服务器包装器,但似乎无法创建 JSON 文件。

  3. const timestamp = Date.now();
    
    const child = childProcess.exec(`node ./index.js --config ./config.js ${flags} --json ${timestamp}.json --console none`);
    
    child.stdout.on('data', (d) => console.log(d.toString()));
    
    child.on('close', () =>{
     res.status(200).json({file: `${timestamp}.json`}); // I want to send this data to frontend
    });
    

我也有兴趣使用 javascript 的其他 library/framework 来实现这一点。

为了在线程之间安全地通信,Electron 前段时间引入了 prelaod.js 脚本的使用。该脚本实际上是主线程和渲染线程之间的看门人。 Electron 的 Context Isolation 页面将对此进行更详细的解释。

除此之外,线程之间的通信由Inter-Process Communication执行。这些 IPC 命令用于启动和侦听 'channels' 上的通信。频道只是您选择的名称以及可以附加的数据(如果需要)。


需要理解和实施的主要组件是 preload.js 脚本。

在这里,您包括 whitelisted 个频道名称。可以使用任何名称格式。任何未包含在这些列表中的频道名称都将被拒绝线程之间的通信。

PS:许多人(包括一些 Electron 文档)直接在预加载脚本中包含实际功能实现。虽然这没有任何问题,但它确实使事情变得比必要的更复杂。使用预加载脚本仅 管理 频道名称,释放在其他地方实施频道名称,这大大简化了事情。 IE:关注点分离。

preload.js(主线程)

// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [],
        // From main to render.
        'receive': [
            'config:data'
        ],
        // From render to main and back again.
        'sendReceive': []
    }
};

// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);

请注意,在列入白名单的 receive 数组中,我包含了频道名称 config:data


现在,在您的主线程中,通过先前指定的通道发送 json 文件的内容。

main.js(主线程)

const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;

const nodePath = require("path");

let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile('index.html')
        .then(() => { window.show(); });
        .then(() => { runConfig(); }); // Now, run your config.js file

    return window;
}

function runConfig() {
    const timestamp = Date.now();
    const child = childProcess.exec(`node ./index.js --config ./config.js ${flags} --json ${timestamp}.json --console none`);

    child.stdout.on('data', (d) => console.log(d.toString()));
    
    child.on('close', () => {
        let jsonData = JSON.parse(nodeFs.readFileSync(`${timestamp}.json`, 'utf8'));
        console.log(jsonData);
        window.webContents.send('config:data', jsonData); // Send the data to the render thread
    });
}

electronApp.on('ready', () => {
    window = createWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

最后,监听要在渲染线程中调用的通道名称。

index.html(渲染线程)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>

    <body></body>
    
    <script>
        window.ipcRender.receive('config:data', (jsonData) => {
            console.log(jsonData);
        });
    </script>
</html>