首页 > 解决方案 > Electron IpcRendrer 未将数据从 Main 发送到 HTML

问题描述

我正在创建一个类似于 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 以下是解释应用程序顺序的屏幕截图。 login.html 的截图 以下是 login.html 中与主进程交互的脚本标签。

<script>
    window.api.receive("fromMain", (data) => {
        let { event, args } = data;
        switch (event) {
            case "onQr":
                generateQrCode(args.url, args.logoFilePath);
                break;
            case "onReady":
                window.location.replace("index.html");
                break;
            default:
                break;
        };
    });
</script>

index.html 的截图如下是login.html中主进程接收和发送的script标签

$("#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");
});

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

// 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);
            });
        }
    }
});

login.html 中控制台日志的屏幕截图 我想要实现的是注销后再次加载登录,应用程序序列重新开始,但是即使触发了接收功能,登录页面在重新加载后也没有从主进程接收任何内容成功导致我怀疑问题出在我使用 window.location.replace 重新加载页面的方式上

标签: electronipc

解决方案


我发现了这个问题,它与 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;
    }
});

推荐阅读