电子 contextBridge returns 未定义

Electron contextBridge returns undefined

我有这 4 个项目文件:

main.js
preload.js
renderer.js
index.html

节点:17.4.0 电子:18.2.0

我试图在我的文件系统上打开一个文本文件,由 renderer.js 的点击事件触发 - 然后将文本文件的内容加载到 [= index.html.

中的 15=] 标签

main.js

const {app, BrowserWindow, ipcMain} = require("electron");
const path = require("path");
const fs = require("fs");

const createWindow = () => {
    // Create the browser window.
    const mainWindow = new BrowserWindow({
         webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            contextIsolation: true,
            nodeIntegration: false
         }
    });
    mainWindow.loadFile(path.join(__dirname, "index.html"));

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

app.on("ready", () => {
    createWindow();

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


function openFile(){
    fs.readFile("logs.txt", "utf8", (err, data) => {
        if (err) {
            console.error(err);
            return "Error Loading Log File";
        }
        console.log(data);
        return data;
    });
}

ipcMain.handle("channel-load-file", openFile);

preload.js

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

contextBridge.exposeInMainWorld("electronAPI", {
    loadFile: () => ipcRenderer.invoke("channel-load-file")
});

renderer.js

const btn = document.querySelector("#btn");
btn.addEventListener("click", e => {
   let data = window.electronAPI.loadFile();
   document.getElementById("main-content").innerText = data;
});

我肯定能在main.js

中看到console.log(data);里面的Log文件的内容

但是,<div id="main-content"></div> 填充了 undefined

我认为我遗漏了其中的一些关键步骤:preload.jsrenderer.js

有人看到事件链在哪里丢失了吗?

(我对流程的任何改进都持开放态度)

在下面的代码中插入 console.log() 表示 handle 内容在 openFile 之前执行 有机会 return 结果。

main.js(主进程)

function openFile() {
    fs.readFile("logs.txt", "utf-8", (err, data) => {
        if (err) {
            console.error(err);
            return "Error Loading Log File";
        }

        console.log('openFile: ' + data); // Testing

        return data;
    });
}

ipcMain.handle('channel-load-file', () => {
    let result = openFile();

    console.log('handle: ' + result); // Testing

    return result;
})

console.log() 结果是...

handle: undefined
openFile: File content...

要解决此问题,让我们将 fs.readFile 从回调更改为承诺,这样我们就可以在 handle.

中为它 await

由于 handle 处理的是承诺,让我们使用语法糖 asyncawait 来简化实现。

main.js(主进程)

function openFile() {
    return new Promise((resolve, reject) => {
        fs.readFile("logs.txt", "utf-8", (error, data) => {
            if (error) {
                console.log('reject: ' + error); // Testing
                reject(error);
            } else {
                console.log('resolve: ' + data); // Testing
                resolve(data)
            }
        });
    });
}

ipcMain.handle('channel-load-file', async (event, message) => {
    return await openFile()
        .then((data) => {
            console.log('handle: ' + data); // Testing
            return data;
        })
        .catch((error) => {
            console.log('handle error: ' + error); // Testing
            return 'Error Loading Log File';
        })
});

最后,让我们修改在 index.html 文件中检索 data 的方式。

PS:让我们也将 .toString() 添加到 returned data(只是为了确定)。

index.html(渲染过程)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <div id="main-content"></div>

        <input type="button" id="button" value="Load File">
    </body>

    <script>
        document.getElementById('button').addEventListener('click', () => {
            window.electronAPI.loadFile()
                .then((data) => {
                    console.log(data); // Testing
                    document.getElementById("main-content").innerText = data.toString();
                });
        })
    </script>
</html>