Electron 中的 nodeIntegration vs preload.js vs IPC答案

作者: 分类: 编程代码 时间:1970-01-01

Electron 中的 nodeIntegration vs preload.js vs IPC答案

nodeIntegration vs preload.js vs IPC in ElectronElectron 中的 nodeIntegration vs preload.js vs IPC

我已经阅读了 Electron 的 context isolation、IPC 和 security 文档,以及 this post about using nodeIntegration 和 this post about preload.js。看起来有很多不同的方法可以完成类似的任务,我不确定哪种方法***(安全、简单等)。

我知道您可以简单地在渲染器进程中启用nodeIntegration 以访问主进程之外的 Node。大多数情况下,每个来源都建议反对。

这就是我感到困惑的地方。 Electron 文档中的一个示例表明您可以执行以下操作。

preload.js

// preload with contextIsolation disabled
window.myAPI = {
  doAThing: () => {}
}

renderer.js

// use the exposed API in the renderer
window.myAPI.doAThing()

preload.js 可以访问 Node API,因此从技术上讲,我可以加载所有 Node 进程,然后在我的渲染器中访问它们。

不过,我也读过 IPC。

main.js 的一部分

ipcMain.on('set-title', (event, title) => {
    const webContents = event.sender
    const win = BrowserWindow.fromWebContents(webContents)
    win.setTitle(title)
})

preload.js

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
    setTitle: (title) => ipcRenderer.send('set-title', title)
})

renderer.js

const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
    const title = titleInput.value
    window.electronAPI.setTitle(title)
});

例如,假设我想使用外部 npm 模块实现一个功能。我是将它合并到 preload.js 中并从我的渲染器中调用它,还是将它合并到 main.js 中,在 preload.js 中使用带有函数通道的 ipcRenderer,然后从我的渲染器中调用它?

【问题讨论】:

标签: javascript node.js npm electron ipc


【解决方案1】:

preload.js 可以访问 Node API,因此从技术上讲,我可以加载所有 Node 进程,然后在我的渲染器中访问它们。

实际上并非总是如此。如果启用了sandbox,则在预加载中只能访问一部分节点 API。 Source:(强调)

附加到沙盒渲染器的预加载脚本仍将有一个可用的 Node.js API 的填充子集

如果您使用的是外部 npm 模块 并且 sandbox 已启用(可能应该启用),那么您将无法在预加载脚本中导入 npm 模块,并且会必须使用ipc并在主进程中触发。

如果sandbox 被禁用,并且您可以从渲染器进程中完成您想要的一切,那么我会说只需在您的预加载中导入模块并直接使用它。

【讨论】:

    【解决方案2】:

    您从上下文隔离文档中粘贴的代码显示了在默认情况下将上下文隔离设置为 true 之前,您可以如何使用在较早版本的电子中暴露(通过预加载)到渲染器的方法和模块。然而,这不是你应该做的。使用contextisolation = true,您需要在预加载中使用上下文桥,因为窗口对象保持隔离。

    因此,您从 IPC 粘贴的代码是您在 2022 年应该采用的方式。除非您真的知道自己在做什么,否则我也不要指望 NodeIntegration。

    关于您的最后一个问题:通常我会采用最小权限的方法。赋予渲染器的权力越少,就越安全。

    我将举一个我最近的项目中的例子。 我需要制作屏幕截图,然后将它们保存在某个地方。为此,我需要Browserwindow.webcontents.capturePage()fs.writeFile(),它们都只对主进程可用。将 fs.writeFile 方法暴露给渲染器肯定会有风险,所以我不这样做。 相反,我保留了在主进程中将屏幕截图写入文件系统的逻辑。但是我想从渲染器(我的 UI)启动屏幕截图。为了尽可能少地公开,我只公开了一个调用 preload 的 contextBridge 中的调用方法的函数,如下所示:

    contextBridge.exposeInMainWorld("ipcRenderer", {
      invokeSnap: async (optionsString: string) =>
        await ipcRenderer.invoke("snap", optionsString),
    });
    

    在 main 中,我有一个监听器来处理传入的请求:

    ipcMain.handle("snap", async (_event, props: string) => {
      // screenshot logic
      return //sth that the renderer will receive back once the process is finished;
    });
    

    渲染器发送请求并处理响应或错误:

    window.ipcRenderer.invokeOpen(JSON.stringify(options))
    .then(...)
    .catch(...)
    
    

    作为副作用,这会将重心放在主进程上,这在更大的项目中可能是不可取的 - idk。也许更有经验的电子开发者可以说得更多。

    【讨论】: