乐趣区

关于electron:Electron-通信新思路

这里指主过程与渲染过程之间的通信。

就像官网文档说的那样:Electron Docs -> Best Practices -> Security

  1. Only load secure content
  2. Disable the Node.js integration in all renderers that display remote content
  3. Enable context isolation in all renderers that display remote content

新版本的要求是,渲染过程不再倡议开启 nodeIntegrationallowRunningInsecureContent 开关,并且对于内部近程内容,强烈建议关上 sandbox 开关。

那么,对于咱们日常开发的本地渲染过程,如何优雅地和主过程之间进行通信,就须要咱们去另辟蹊径了。

好在,Electron 尽管关上了一扇窗户,然而又关上了另一扇新的窗户,在 preload 中预加载的 js 环境中,就给咱们留下一条后路,因为在预加载 js 外面,require('electron') 是被容许的,而且还提供了一个工具,能够使咱们将这个文件里定义好的变量或办法不便地“挪移”到渲染过程的一般 js 环境里去,这个工具就是 —— contextBridge。

一段官网示例如下:

// Preload (Isolated World)
const {contextBridge, ipcRenderer} = require('electron')

contextBridge.exposeInMainWorld('electron', {doThing: () => ipcRenderer.send('do-a-thing')
});

在渲染过程里这样应用:

// Renderer (Main World)
window.electron.doThing()

其中,'electron' 只是轻易起的名称,并不意味着整个 electron 对象“挪移”到了渲染过程。

问题是,当初,尽管能够在渲染过程里调用 window.electron.doThing() 进行通信了,然而如果我通信的 channel 太多,是不是要一个个地定义 doXXX() 办法?岂不太麻烦了!

改良一:

一种思路是,将 ipcRenderer 对象“挪移”到了渲染过程里,咱们试试看

// Preload (Isolated World)
const {contextBridge, ipcRenderer} = require('electron');

contextBridge.exposeInMainWorld('electron', {ipc: ipcRenderer});

而后再渲染过程里:

// Renderer (Main World)
console.log(window.electron.ipc);

发现,这里的 ipc,也有 invoke,有 send,也有 sendSync,就是没有 on,应用这种形式,仿佛只能进行 Renderer --> Main 方向的通信,反之则不能够,间隔实现咱们的工作还有一半间隔,如果说我的项目只须要这繁多方向的通信也就罢了,然而如果想要双向通信的话,就要开始新的尝试了。于是有了:

改良二:

我的思路是,既然 contextBridge 不给我裸露 ipcRendereron 办法,那我就本人写一个,于是有了上面的代码:

// Preload (Isolated World)
const {contextBridge: bridge, ipcRenderer: ipc} = require('electron');

bridge.exposeInMainWorld('ipc', {send: (channel, data) => ipc.send(channel, data),
  on: (channel, fun) => ipc.on(channel, fun),
});

渲染过程里:

// Renderer (Main World)
console.log(window.ipc);

发现有 sendon 办法,赶快试试:

主过程:

// Main
ipc.on('ping', (e, data) => {console.log('received', data);
  setTimeout(() => {
    const count = data + 1;
    e.sender.send('pong', count);
    console.log('sent', count);
  }, 2000);
});

// -->
// received 1
// sent 2

渲染过程:

// Renderer (Main World)
document.addEventListener('DOMContentLoaded', () => {
  const ipc = window.ipc;
  ipc.send('ping', 1);
  ipc.on('pong', (e, data) => {console.log('received', data);
  });
});

// -->
// received 2

至此,胜利!

残缺的例子,参见我的 Github Demo

题外话

Isolated WorldMain World

ELectron 官网把预加载 js 中的环境称为 Isolated World,而把渲染过程中一般 js 中的环境称为 Main World,集体推测是因为其实现形式,预加载 js 的实现形式其实就是将这个文件当作浏览器插件的模式插入到渲染过程的渲染窗口,所以渲染过程中一般 js 为“主”,预加载 js 为“辅”,有了以上称呼。

文章原公布与《羽痕爱编程》,禁止非自己转载,侵权必究。

退出移动版