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

发布于 2025-01-11 11:46:41 字数 420 浏览 0 评论 0原文

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

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

然后像这样获取/设置它:

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

我一直在尝试在预加载中公开api对象。 js 来实现相同的旧功能,但没有成功。
在不设置nodeIntegration:true,contextIsolation:false的情况下如何实现此目的?

I'm updating an entire app written a few years ago using Electron v1.8.8. As far as I know, Electron changed its paradigm and now the default expected way of communicating between the main and renderer process is using a module in the middle named preload.js.
To get/set global variables in the old way you would first require the remote module:

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

And then getting/setting it like so:

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

I've been trying to expose an api object in preload.js to achieve the same old functionality, but with no success.
How I would achieve this without setting nodeIntegration: true, contextIsolation: false?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

简单爱 2025-01-18 11:46:41

主线程渲染线程 需要了解 <一个href="https://www. Electronjs.org/docs/latest/tutorial/ipc" rel="nofollow noreferrer">进程间通信 和 上下文隔离

你是正确的, 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:saysHi 和 mainThread: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,这是 contextBridge.exposeInMainWorld 行下面的 preload.js 脚本中使用的结构。您可以将 ipcRenderreceive / send / invoke 重命名为您喜欢的任何名称,但如果这样做,您将在使用“html js”端时也使用相同的引用。

To send messages between the main thread and a render thread requires the understanding of Inter-Process Communication and Context Isolation.

You are correct in that preload.js acts like a middle man. From my experience, using preload.js as a script whose sole purpose is to communicate data between the main and render threads via the use of defined channels allows for a simple, easy to read, separation of concern. IE: Don't place domain specific functions within your preload.js script. Only add functions that are used for the purpose of performing specific methods of communication.

Below is a typical preload.js file. I have defined a couple of channels to communicate between your main thread and your render thread. PS: You can use any name / naming convention to name your channels.

preload.js (main thread)

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

Whilst your main.js file may look a little different than that shown below, the main points of interest are the reception and transmission of the defined channels renderThread:saysHi and mainThread:saysHi.

main.js (main thread)

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

Your index.html file will import your Javascript file which would contain the code to listen out and send messages on the specified channels.

index.html (render thread)

<!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 (render thread)

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.');
    });
})();

In the above code, the global object window contains ipcRender.send and ipcRender.receive which is the structure used in your preload.js script underneath the line contextBridge.exposeInMainWorld. You can rename ipcRender and receive / send / invoke to anything you like, though if you do, you would also use the same reference when using it 'html js' side.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文