在带有 Socket.IO 的 ReactJS 应用程序上使用 Electron 的 ipcRenderer 时出错

问题描述 投票:0回答:1

我有一个使用 Electron Forge 应用程序以及 ReactJS 和 Socket.IO 的场景:

预加载.js:

import { contextBridge, ipcRenderer } from "electron";

window.addEventListener("DOMContentLoaded", () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector);
    if (element) element.innerText = text;
  };

  for (const type of ["chrome", "node", "electron"]) {
    replaceText(`${type}-version`, process.versions[type]);
  }
});

let state = {
  config: {},
  store: {},
};

// Function to update the state
const updater = (key, value) => {
  state[key] = value;

  ipcRenderer.send("updater", {
    key: key,
    value: value,
  });
};

const URL = `ws://localhost:3010`; // Test port

const io = require("socket.io-client");
const socket = io(URL);

socket.on("connect", () => {
  console.log("Connect...");
});

socket.on("upload", (msg) => {
  let json = JSON.parse(msg);
  let key = json.key;
  let value = json.value; 

  if (key == "config") updater("config", value);
  if (key == "store") updater("store", value);
});

ipcRenderer.on('command', (payload) => {
  socket.emit("command", JSON.stringify(payload));
});

contextBridge.exposeInMainWorld("stateAPI", {
  state: state,
  updater: updater, // Expose the update function
});

main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require("electron");
const path = require("path");

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 640,
    height: 480,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
      webSecurity: false, // Need this to use Socket.io? Is there other way to do it?
    },
  });

  // and load the index.html of the app.
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

  // if embedded Linux set fullscreen
  if (process.arch.includes("arm")) {
    mainWindow.setFullScreen(true);
  }

  // Open the DevTools.
  mainWindow.webContents.openDevTools();
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  createWindow();

  app.on("activate", function () {
    console.log("-----> activate");
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", function () {
  console.log("-----> window-all-closed");
  if (process.platform !== "darwin") app.quit();
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

process.on("uncaughtException", (error) => {
  console.error("Uncaught Exception:", error);
  fs.writeFileSync(
    "/home/torizon/app/error.log",
    `Uncaught Exception: ${error.message}\n${error.stack}`
  );
});

我的反应代码:index.js

import React, { useEffect, useState } from "react";
import { ipcRenderer } from "electron"; **<=== This is causing the error**


const command = (payload) => {
  ipcRenderer.send("command", payload);
};

const Home = () => {
  const [state, setState] = useState(window.stateAPI.state);

  useEffect(() => {
    const handleStateUpdate = (key, value) => {
      let clone = Object.assign({}, state);
      clone[key] = value;
      setState(clone);
    };

    ipcRenderer.on("state_update", handleStateUpdate);

    // Clean up the listener on component unmount
    return () => {
      ipcRenderer.removeListener("state_update", handleStateUpdate);
    };
  }, []);

  return (
    <div>
      <h1>{state.config}</h1>
      <h1>{state.store}</h1>
      <button onClick={() => command("Test")}>Click me</button>
    </div>
  );
};

export default Home;

webpack 配置:webpack.main.config.js

module.exports = {
  entry: './src/main.js',
  module: {
    rules: require('./webpack.rules'),
  },
};

webpack.renderer.config.js

const rules = require("./webpack.rules");

rules.push({
  test: /\.css$/,
  use: [
    {
      loader: "style-loader",
      options: {
        esModule: false,
      },
    },
    {
      loader: "css-loader",
      options: {
        esModule: false,
        modules: {},
      },
    },
  ],
});
rules.push({
  test: /\.(png|jpg|jpeg|gif)$/i, // Add support for image formats
  type: 'asset/resource', // This will emit a separate file for the image
});

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules,
  },
};

当我尝试在 ReactJs 应用程序中导入

ipcRenderer
时,出现以下错误:

index.js:1 Uncaught Error: Cannot find module 'fs'
    at webpackMissingModule (index.js:1:1)
    at eval (index.js:1:1)
    at ./node_modules/electron/index.js (index.js:62:1)
    at __webpack_require__ (index.js:8878:32)
    at fn (index.js:9073:21)
    at eval (index.js:7:66)
    at ./src/components/home/index.js (index.js:945:1)
    at __webpack_require__ (index.js:8878:32)
    at fn (index.js:9073:21)
    at eval (app.jsx:5:74)

如果我取消注释,则不会出现错误,但我无法更改 Electron 和我的应用程序之间的

state
数据。

什么可能导致此问题以及如何解决?感谢帮助。

reactjs node.js webpack socket.io electron
1个回答
0
投票

您的代码至少存在两个问题。

首先,您会收到此错误,因为您尝试使用 webpack 编译 Node.js 代码(即此处通过导入

electron
来实现
ipcRenderer
),并且没有为其配置(请参阅 webpack 的
target
配置) )。 但这并不意味着您应该更改其配置。您的 React 代码位于 渲染器进程,出于安全原因,您不应该在此进程中使用 Node.js。

要解决第一个问题,您需要使用 preload 文件来公开您需要的内容(您需要的内容,而不是整个 IPC API)。您可以在官方 IPC 文档 以及我在此处链接的其他文档中找到大量示例。

解决该问题后,您的预加载文件可能会出现

module not found
错误。默认情况下,出于“安全原因”,预加载脚本是“沙盒”的,这意味着您只能导入 Electron 和 Node 内置模块的特定子集。如果您想导入 socket.io-client 并遵循安全建议,您需要在您的 主进程上执行此操作。
    
© www.soinside.com 2019 - 2024. All rights reserved.