electron是nodejs+chrome內(nèi)核+native層的集成,類似chrome瀏覽器每個(gè)頁(yè)面都屬于一個(gè)進(jìn)程(稱為渲染進(jìn)程),另外還有主進(jìn)程,其提供了如下進(jìn)程間通信機(jī)制:
- IPCMain IPCRender通信模塊,其中IPCMain用于主進(jìn)程,IPCRender用于渲染進(jìn)程,這兩者都是node's EventEmiter實(shí)例的封裝;
-
渲染進(jìn)程->主進(jìn)程
渲染進(jìn)程通過(guò)IPCRender.send發(fā)送異步消息,IPCRender.sendSync發(fā)送同步消息;主進(jìn)程通過(guò)IPCMain.on接收消息并通過(guò)event.sender.send響應(yīng)異步消息,event.returnValue響應(yīng)同步消息。
通過(guò)IPCMain與IPCRender模塊可實(shí)現(xiàn)client-server模型,主進(jìn)程作為server端注冊(cè)消息并提供業(yè)務(wù)服務(wù)(如創(chuàng)建窗口、托盤、數(shù)據(jù)持久化訪問(wèn)及設(shè)置等),渲染進(jìn)程作為client端以發(fā)送消息,因此可建立消息與業(yè)務(wù)的分離,業(yè)務(wù)層注冊(cè)可實(shí)現(xiàn)的消息并處理響應(yīng)結(jié)果及返回?cái)?shù)據(jù);
// In main process.
const {ipcMain} = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.sender.send('asynchronous-reply', 'pong')
})
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.returnValue = 'pong'
})
// In renderer process (web page).
const {ipcRenderer} = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')
主進(jìn)程->渲染進(jìn)程
主進(jìn)程通過(guò)BrowserWindow.webcontent.send發(fā)送消息,通過(guò)在主進(jìn)程管理所有窗口對(duì)象,可遍歷所有窗口對(duì)象的webcontent屬性來(lái)發(fā)送消息,進(jìn)而可實(shí)現(xiàn)主進(jìn)程向渲染進(jìn)程的廣播消息
注意,
webContents.on監(jiān)聽(tīng)的是已經(jīng)定義好的事件,如上面的did-finish-load。要監(jiān)聽(tīng)自定義的事件還是通過(guò)ipcMain和ipcRenderer。渲染進(jìn)程的監(jiān)聽(tīng)事件回調(diào)函數(shù)中,也可以通過(guò)
event.sender來(lái)向主進(jìn)程發(fā)送消息。這個(gè)對(duì)象只是ipcRenderer的引用(event.sender === ipcRenderer)。因此,event.sender發(fā)送的消息在主進(jìn)程中還是需要通過(guò)ipcMain.on方法來(lái)監(jiān)聽(tīng),而不是通過(guò)webContents.on方法。
-
// In the main process. const {app, BrowserWindow} = require('electron') let win = null app.on('ready', () => { win = new BrowserWindow({width: 800, height: 600}) win.loadURL(`file://${__dirname}/index.html`) win.webContents.on('did-finish-load', () => { win.webContents.send('ping', 'whoooooooh!') }) })<!-- index.html --> <html> <body> <script> require('electron').ipcRenderer.on('ping', (event, message) => { console.log(message) // Prints 'whoooooooh!' }) </script> </body> </html>
- electron.remote模塊
渲染進(jìn)程可加載remote模塊,并訪問(wèn)主進(jìn)程的模塊或者app對(duì)象,從而使用主進(jìn)程的模塊或者獲取變量,如BrowserWindow、global對(duì)象。
進(jìn)程間通信機(jī)制封裝
可通過(guò)electron提供的通訊機(jī)制接口封裝一套通訊模塊,用于主進(jìn)程與渲染進(jìn)程間通信,基于client-server、訂閱/發(fā)布及廣播模型構(gòu)建一套完成的進(jìn)程間消息機(jī)制,消息機(jī)制流程(可參考)如下:

渲染進(jìn)程加載本地js文件
渲染進(jìn)程需要加載本地的IPC Clientjs文件已實(shí)現(xiàn)進(jìn)程間通信,可通過(guò)如下方式實(shí)現(xiàn):
使用browserwindow.webPreferences.preload指定需要加載的js文件
使用browserwindow.webcontent.executejavascript()接口
使用executejavascript方式存在時(shí)序問(wèn)題,因其加載晚于webcontent加載渲染頁(yè)面,而preload方式要早于webcontent頁(yè)面加載;
BrowserWindow對(duì)象都有唯一的窗口ID(可通過(guò)getCurrentWindow來(lái)獲?。?,若是基于業(yè)務(wù)需要設(shè)置綁定不同的ID,可通過(guò)此唯一的ID綁定需要設(shè)置的ID,并通過(guò)remote模塊獲取對(duì)應(yīng)關(guān)系,以解決BrowserWindow對(duì)象通過(guò)executejavascript時(shí)序問(wèn)題;