Electron IpcRendrer 未将数据从 Main 发送到 HTML

分享于2022年07月17日 electron ipc 问答
【问题标题】:Electron IpcRendrer 未将数据从 Main 发送到 HTML(Electron IpcRendrer not sending data from Main to HTML)
【发布时间】:2021-12-27 19:20:39
【问题描述】:

我正在创建一个类似于 WhatsAppWeb 的 WhatsApp 客户端。我使用 IPC 在 HTML 和 main 之间使用以下 preload.js 进行通信

const { ipcRenderer, contextBridge } = require("electron");


contextBridge.exposeInMainWorld("api", {
    send: (channel, data) => {
        // whitelist channels
        let validChannels = ["toMain"];
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, data);
        }
    },
    receive: (channel, func) => {
        let validChannels = ["fromMain"];
        if (validChannels.includes(channel)) {
            ipcRenderer.on(channel, (_, ...args) => func(...args));
        }
    }
});

以下是main.js

const path = require("path");
const client = require("./src/client").instance;
const { app, ipcMain, BrowserWindow } = require("electron");

let win;
function createWindow() {
    win = new BrowserWindow({
        width: 1280,
        height: 800,
        icon: path.join(__dirname, "views", "images", "logo.png"),
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "src", "preload.js"),
        }
    });

    win.maximize();
    win.loadFile(path.join("views", "login.html"));
    win.webContents.openDevTools();
}

app.whenReady().then(() => {
    createWindow();

    app.on("activate", () => {
        if (BrowserWindow.getAllWindows().length === 0)
            createWindow();
    });
});

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

client.addListner("qr", (url) => {
    // this works fine and logging the URL after page change using a console log prints a valid qrcode url
    if (!client.isReady()) {
        let logoFilePath = path.join(__dirname, "views", "images", "logo.png");
        win.webContents.send("fromMain", {
            event: "onQr",
            args: {
                url: url,
                logoFilePath: logoFilePath
            }
        });
    }
});

client.login();

ipcMain.on("toMain", (_, data) => {
    let { event, args } = data;
    switch (event) {
        case "logout":
            client.logout().then(() => client.login());
        default:
            break;
    }
});

我有 2 个 HTML 页面 login.html 和 index.html 以下是解释应用程序顺序的屏幕截图。 screenshot of login.html 以下是login.html中与主进程交互的脚本标签。


screenshot of index.html 以下是login.html中向主进程接收和发送的脚本标签

$("#logoutBtn").click((event) => {
    event.preventDefault();
    window.api.send("toMain", {
        event: "logout",
        args: {}
    });
    // I think the problem may be here on page redirection becuase
       it almost feels like the script doesn't run at all in login.html after being reloaded
    window.location.replace("login.html");
});

按下注销按钮后,登录页面正常加载,后台进程正常生成二维码,但不再触发recieve功能。 尝试通过在预加载接收功能中添加控制台日志来检查发送功能是否是主进程正在工作,在控制台中会得到以下结果。

// preload after modification
const { ipcRenderer, contextBridge } = require("electron");


contextBridge.exposeInMainWorld("api", {
    send: (channel, data) => {
        // whitelist channels
        let validChannels = ["toMain"];
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, data);
        }
    },
    receive: (channel, func) => {
        let validChannels = ["fromMain"];
        if (validChannels.includes(channel)) {
            // the console logs works fine
            console.log(">>>>>>>>>>> recieving");
            console.log(func);
            // this line doesn't do anything in the second page load
            ipcRenderer.on(channel, (_, ...args) => {
                func(...args);
            });
        }
    }
});

screenshot of console logs in login.html 我想要实现的是,注销后登录再次加载应用程序序列重新开始但是登录页面在重新加载后没有从主进程收到任何内容,即使成功触发了接收功能,这导致我怀疑这个问题妨碍我使用 window.location.replace 重新加载页面


【解决方案1】:

我发现了这个问题,它与 IPCRenderer 或页面重新加载无关,而是我的错误。我没有将主进程中的客户端对象更新为新客户端,所以生成了二维码,但由于旧客户端处于就绪状态,因此从未发送过。

// new main.js
const path = require("path");
// import the class instead of an instance to be able to create an new object whenever user logs out
let WhatsAppClient = require("./src/client");
const { app, ipcMain, BrowserWindow } = require("electron");

let win;
function createWindow() {
    win = new BrowserWindow({
        width: 1280,
        height: 800,
        icon: path.join(__dirname, "views", "images", "logo.png"),
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            enableRemoteModule: false,
            preload: path.join(__dirname, "src", "preload.js"),
        }
    });

    win.maximize();
    win.loadFile(path.join("views", "login.html"));
}

app.whenReady().then(() => {
    createWindow();

    app.on("activate", () => {
        if (BrowserWindow.getAllWindows().length === 0)
            createWindow();
    });
});

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

// moved listners to a setup function to call it when ever creating a new client
const setupClientListners = (client) => {
    client.addListner("qr", (url) => {
        // the problem was here as I were using the old client object which had a ready state of true so the qr was generate but never sent to the HTML page
        if (!client.isReady()) {
            let logoFilePath = path.join(__dirname, "views", "images", "logo.png");
            win.webContents.send("fromMain", {
                event: "onQr",
                args: {
                    url: url,
                    logoFilePath: logoFilePath
                }
            });
        }
    });

    client.addListner("ready", () => {
        client.isReady(true);
        win.webContents.send("fromMain", {
            event: "onReady",
            args: {}
        });
    });
};

// setting up client
let client = new WhatsAppClient();
setupClientListners(client);
client.login();

ipcMain.on("toMain", async (_, data) => {
    let { event, args } = data;
    switch (event) {
        case "logout":
            await client.logout();
            // delete old client and create a new one
            delete client;
            client = new WhatsAppClient();
            setupClientListners(client);
            client.login();
        default:
            break;
    }
});