Electron 桌面应用有两个进程,分别为主进程和渲染进程。
一、主进程和渲染进程介绍
主进程(Main Process)
- 应用启动时,会创建个主进程
- 一个应用有且只有一个主进程
- 只有主进程可以进行
GUI
的 API 操作,即调用Native APIs
渲染进程(Renderer Process)
- Windows 中展示的界面通过渲染进程表现,DOM 操作,node js
- 一个应用可以有多个渲染进程
- 要通过主进程才可以访问原生 API(Native APIs),要先跟主进程进行 ipc 通信
二、渲染进程发消息给主进程
其实主要就是两个 API 的使用: 渲染进程里的 ipcRenderer
与 主进程里的 ipcMain
渲染进程发异步消息给主进程
// 渲染进程脚本
const { ipcRenderer } = require('electron')
// 发送异步消息
btns[0].addEventListener('click', () => {
ipcRenderer.send('msg1', '这是一条来自于异步的消息')
})
// 监听消息
ipcRenderer.on('msg1Re', (ev, data) => {
console.info(data)
})
// 主进程脚本
const { ipcMain } = require('electron')
ipcMain.on('msg1', (ev, data) => {
console.info(data)
// 发送消息给渲染进程
ev.sender.send('msg1Re', '这是一条来自主进程的反馈消息')
})
渲染进程发同步消息给主进程
// 渲染进程脚本
const { ipcRenderer } = require('electron')
// 发送同步消息
btns[1].addEventListener('click', () => {
const result = ipcRenderer.sendSync('msg2', '这是一条来自于同步的消息')
console.info(result)
})
// 主进程脚本
const { ipcMain } = require('electron')
ipcMain.on('msg2', (ev, data) => {
console.info(data)
// 反馈消息
ev.returnValue = '这是一条来自主进程的同步反馈消息'
})
三、主进程发消息给渲染进程
// 主线程脚本
BrowserWindow.getFocusedWindow().webContents.send(
'mtp',
'主进程发送消息给渲染进程'
)
// 渲染进程脚本
ipcRenderer.on('mtp', (ev, data) => {
console.info(data)
})
四、渲染进程间通信
基于本地存储的渲染进程间通信
即采用 localStorage
机制
借助主进程,进行不同渲染进程间的通信
// 发起消息的渲染进程
ipcRenderer.send('mti', '这是条来自于 modal 的消息')
// 主进程
ipcMain.on('mti', (ev, data) => {
// 通过 id 获取到对应的渲染进程,然后消息传递
BrowserWindow.fromId(mainId).webContents.send('mti2', data)
})
// 接收消息的渲染进程
ipcRenderer.on('mti2', (ev, data) => {
console.info(data)
})
使用 sendTo
前提是要知道另一渲染窗口的 webContents 对应的 id
这边采用 global 存储窗口 ID
const modalMain = new BrowserWindow({
width: 200,
height: 200,
parent: BrowserWindow.fromId(mainId), // 这样关闭父窗口,则子窗口会一并关闭
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
})
global.sharedObject = {
modalMainWebContentsId: modalMain.webContents.id
}
然后在渲染进程里通过 getGlobal 来获取该 ID 值,并通过 sendTo 来发送消息
let sharedObject = getGlobal('sharedObject')
let modalMainWebContentsId = sharedObject.modalMainWebContentsId
ipcRenderer.sendTo(modalMainWebContentsId, 'do-some-work', 1)
这样其他渲染进程就通过监听 do-some-work 来获取消息
ipcRenderer.on('do-some-work', (e, data) => {
console.info(data)
})
五、remote
GUI 相关的模块 (如 dialog、menu 等) 仅在主进程中可用, 在渲染进程中不可用。为了在渲染进程中使用它们, 必须使用 remote 模块。这样就可以调用 main 进程的方法,而不用显示发送进程间的信息。
不同的 Electron 版本,remote 获取方式不同。
比如在渲染进程里创建个窗口:
In electron > 14.0.0:
- 安装 @electron/remote
- 定义 webPreferences 属性
webPreferences: {
nodeIntegration: true, // 配合 contextIsolation 才会起作用, 使得渲染进程可以使用 node API
contextIsolation: false
}
- 主进程脚本:
require('@electron/remote/main').initialize()
mainWindow.loadFile('index.html')
const contents = mainWindow.webContents
require('@electron/remote/main').enable(contents)
- 子进程脚本
const { BrowserWindow } = require('@electron/remote')
In electron == 14.0.0:
参照官文:https://www.electronjs.org/docs/breaking-changes#%E5%B7%B2%E5%BA%9F%E5%BC%83%EF%BC%9A-remote-%E6%A8%A1%E5%9D%97
实际上就是不用在主进程里 enable webContents
In electron < 14.0.0:
- 主进程脚本:
{ webPreferences: { enableRemoteModule: true } }
- 渲染进程脚本
const { BrowserWindow } = require('electron').remote