在现代独立渲染器进程 Electron 中使用 remote.getGlobal 变量

Using remote.getGlobal variables in modern isolated renderer process Electron

我正在更新几年前使用 Electron v1.8.8 编写的整个应用程序。据我所知,Electron 改变了它的范式,现在主进程和渲染进程之间默认的预期通信方式是在中间使用一个名为 preload.js.[= 的模块。 23=] 要以旧方式 get/set 全局变量,您首先需要远程模块:

var { remote } = require('electron');

然后getting/setting像这样:

remote.getGlobal('sharedObj').get('user')

我一直在尝试在 preload.js 中公开一个 api 对象以实现相同的旧功能,但没有成功。
我如何在不设置 nodeIntegration: true, contextIsolation: false?

的情况下实现这一目标

main thread and a render thread requires the understanding of Inter-Process Communication and Context Isolation 之间发送消息。

你是对的,preload.js 就像一个中间人。根据我的经验,使用 preload.js 作为脚本,其唯一目的是通过使用定义的通道在主线程和渲染线程之间传递数据,这样可以实现简单、易于阅读的关注点分离。 IE:不要在 preload.js 脚本中放置域特定函数。仅添加用于执行特定通信方法的功能。

下面是一个典型的 preload.js 文件。我已经定义了几个通道来在您的主线程和渲染线程之间进行通信。 PS:您可以使用任何名称/命名约定来命名您的频道。

preload.js(主线程)

const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [
            'renderThread:saysHi'
        ],
        // From main to render.
        'receive': [
            'mainThread:saysHi'
        ],
        // 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);
            }
        }
    }
);

虽然您的 main.js 文件看起来可能与下面显示的略有不同,但主要关注点是定义频道 renderThread:saysHimainThread:saysHi 的接收和传输。

main.js(主线程)

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

const nodePath = require("path");

let appWindow;

function createAppWindow() {
    const appWindow = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        fullscreen: false,
        resizable: true,
        movable: true,
        minimizable: true,
        maximizable: true,
        enableLargerThanScreen: true,
        closable: true,
        focusable: true,
        fullscreenable: true,
        frame: true,
        hasShadow: true,
        backgroundColor: '#fff',
        show: false,
        icon: nodePath.join(__dirname, 'icon.png'),
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            worldSafeExecuteJavaScript: true,
            enableRemoteModule: false,
            devTools: (! electronApp.isPackaged),
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    appWindow.loadFile('index.html')
        .then(() => {
            // Main thread saying hi to the render thread.
            appWindow.webContents.send('mainThread:saysHi', 'Hello from the main thread.'); })
        .then(() => { 
            appWindow.show(); })

    return appWindow;
}

// Listen for the render thread saying hi.
electronIpcMain.on('renderThread:saysHi', (event, message) => {
    console.log(message); });
}

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

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

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

您的 index.html 文件将导入您的 Javascript 文件,该文件将包含在指定频道上收听和发送消息的代码。

index.html(渲染线程)

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

    <body>
        <div id="main-thread-message"></div>

        <input type="button" id="render-thread-button" value="Click to say hi to the main thread">
    </body>

    <script type="module" src="index.js"></script>
</html>

index.js(渲染线程)

let mainThreadMessage = document.getElementById('main-thread-message');
let renderThreadButton = document.getElementById('render-thread-button');

// IIFE - Immediately Invoke Function Expression
(function() {
    window.ipcRender.receive('mainThread:saysHi', (message) => {
        mainThreadMessage.textContent = message;
    });

    renderThreadButton.addEventLister('click', () => {
        window.ipcRender.send('renderThread:saysHi', 'Hello from the render thread.');
    });
})();

在上面的代码中,全局对象 window 包含 ipcRender.sendipcRender.receive,这是 preload.js 脚本中第 [=30= 行下方使用的结构].您可以将 ipcRenderreceive / send / invoke 重命名为您喜欢的任何名称,但如果这样做,您在使用它时也会使用相同的引用 'html js'边。