Electron桌面應用中有兩個進程,分別是Main
主進程和Renderer
渲染進程。兩個進程間有多種方法進行通信。
一、主進程和渲染進程
1、主進程Main
main.js在啟動應用后就創建了一個主進程main process,它可以通過electron中的一些模塊直接與原生GUI(在你的應用窗口)交互。
2、渲染進程Renderer
僅啟動主進程并不能給你的應用創建應用窗口。窗口是通過main文件里的主進程調用叫BrowserWindow
的模塊創建的。每個頁面都是運行在自己的進程里,這些進程我們稱之為渲染進程。渲染進程會在窗口中渲染出web頁面(引用了CSS,JavaScript,圖片等的HTML文件)。web頁面是Chromium渲染的,因為各系統下標準是統一的的,所以兼容性很好。
3、主進程于渲染進程的關系
主進程通過構造BrowserWindow
實例來創建頁面。每個 BrowserWindow
實例都在自己的渲染進程里運行頁面。當一個 BrowserWindow
實例被銷毀后,相應的渲染進程也會被終止。
主進程管理所有頁面和與之對應的渲染進程。每個渲染進程都是相互隔離的,并且只知道運行在該進程里的頁面。
在頁面里調用本地GUI是不允許的,因為在Web頁面里管理本地GUI資源是非常危險而且容易造成資源泄露。如果你想在網頁里進行GUI操作,該頁面的渲染進程必須與主進程進行通訊,請求主進程進行相關的 GUI 操作。
二、幾種通信方式
1、ipc
模塊
-
ipcMain
模塊 -
ipcRenderer
模塊
ipcMain
模塊和ipcRendere
r是類EventEmitter
的實例。
在Electron項目中,使用require
來引入這個模塊,而在Electron-vue中,由于使用了webpack,可以使用import
引入。
1.1、ipcMain
監聽 channel,當接收到新的消息時 listener 會以 listener(event, args...) 的形式被調用。
添加一次性的 listener。當且僅當下一個消息發送到 channel 時 listener 才會被調用,隨后listener會被移除。
從監聽器數組中移除監聽 channel 的指定 listener。
刪除所有監聽者,或特指的 channel 的所有監聽者。
1.2、ipcRenderer
監聽 channel, 當新消息到達,將通過 listener(event, args...) 調用 listener
-
ipcRenderer.once(channel, listener)
-
ipcRenderer.removeListener(channel, listener)
-
ipcRenderer.removeAllListeners(channel)
-
ipcRenderer.send(channel[, arg1][, arg2][, ...])
通過 channel
發送異步消息到主進程,可以攜帶任意參數。 在內部,參數會被序列化為 JSON,因此參數對象上的函數和原型鏈不會被發送。
主進程可以使用 ipcMain
監聽channel來接收這些消息。
-
ipcRenderer.sendSync(channel[, arg1][, arg2][, ...])
通過 channel
發送同步消息到主進程,可以攜帶任意參數。 在內部,參數會被序列化為 JSON,因此參數對象上的函數和原型鏈不會被發送。
主進程可以使用 ipcMain
監聽channel來接收這些消息,并通過 event.returnValue
設置回復消息。
-
ipcRenderer.sendTo(webContentsId, channel, [, arg1][, arg2][, ...])
通過channel
向具有WebContentSid
的窗口發送消息
-
ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])
就像 ipcRenderer.send
,不同的是消息會被發送到 host 頁面上的 <webview>
元素,而不是主進程。
1.3、Renderer
進程向Main
進程發送消息,Main
進程進行回復(ipc模塊沒有Main進程向Renderer模塊發送的方法)
- 發送消息時,事件名稱為
channel
(如例子中的asynchronous-message)
1.3.1、異步消息
- 主進程將異步消息發送回發件人,需要使用event.sender.send(...)
// Renderer進程(為Vue頁面或main.js頁面)
import {ipcRenderer} from 'electron';
import Vue from 'vue';
<template>
<div>{{this.msg}}</div>
</template>
<script>
ipcRender.on('asynchronous-reply', function (event, arg) { // 接收到Main進程返回的消息
const message = `異步消息回復: ${arg}`
})
export default{
mounted(){
ipcRenderer.send('asynchronous-message', 'ping') //給主進程發送消息“ping”
}
}
//Main進程(為js文件)
import {ipcMain} from 'electron';
ipcMain.on('asynchronous-message',(event, arg) => { // arg為接受到的消息
event.sender.send('asynchronous-reply', 'pong'); // 返回一個'pong'
})
1.3.2、同步消息
可以使用ipc
模塊在進程之間發送同步消息. 但請注意, 此方法的同步特性意味著它在完成任務時會阻止其他操作
- 主進程回復同步信息時,需要設置event.returnValue
// Renderer進程
import {ipcRenderer} from 'electron';
import Vue from 'vue';
<template>
<div>{{this.msg}}</div>
</template>
<script>
ipcRender.on('synchronous-reply', function (event, arg) { // 接收到Main進程返回的消息
const message = `異步消息回復: ${arg}`
})
export default{
mounted(){
ipcRenderer.sendSync('synchronous-message', 'ping') //給主進程發送消息“ping”
}
}
//Main進程
mport {ipcMain} from 'electron';
ipcMain.on('synchronous-message',(event, arg) => { // arg為接受到的消息
event.returnValue = 'pong' // 返回一個'pong'
})
2、webContents.send方法(Main進程主動向Renderer進程發送消息)
webContents.send
是BrowserWindow類
的一個方法,BrowserWindow類
用于創建一個程序窗口,實例化之后,設置窗口寬高,并設置其loadURL
(加載的頁面),一個窗口就創建成功并開始顯示。通信方法如下
mainWindow.webContents.send('list', res.data);
// mainWindow是一個BrowserWindow實例
在渲染進程中,依舊是使用ipcRenderer
對消息進行接收
ipcRenderer.on('list', (e, msg) => {
console.log(msg);
});
}
3、remote模塊
remote模塊支持RPC風格的通信,在渲染進程中獲取主進程創建的一些全局對象和應用信息,還可以調用主進程所提供的一些方法,如重啟應用、操作渲染進程等。
在Electron中, GUI 相關的模塊 (如 dialog
、menu
等) 僅在主進程中可用, 在渲染進程中不可用。 為了在渲染進程中使用它們, 必須使用 remote
模塊,。你可以調用 main 進程對象的方法, 而不必顯式發送進程間消息, 類似于 Java 的 RMI 。
- 從渲染進程創建瀏覽器窗口的一個例子
const { BrowserWindow } = require('electron').remote;
let win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://github.com');
注意: 反向操作( 從主進程訪問渲染進程) ,可以使用webContents.executeJavascript
3.1遠程對象
remote 模塊返回的每個對象 (包括函數) 表示主進程中的一個對象 (我們稱它為遠程對象或遠程函數)。 當調用遠程對象的方法時, 調用遠程函數, 或者使用遠程構造函數 (函數) 創建新對象時, 實際上是在發送同步進程消息。
在上面的示例中, BrowserWindow
和 win
(browser-window. md) 都是遠程對象, new BrowserWindow 在渲染過程中沒有創建 BrowserWindow 對象。 取而代之的是,它在主進程中創建了一個 BrowserWindow對象,并且在渲染進程中返回相應的遠程對象,即win對象。
請注意只有可枚舉屬性才能通過 remote 進行訪問。
3.2遠程對象的生命周期
Electron 確保在渲染進程中的遠程對象存在(換句話說,沒有被垃圾收集),那主進程中的對應對象也不會被釋放。當遠程對象被垃圾收集之后,主進程中的對應對象才會被取消關聯。如果遠程對象在渲染進程泄露了(即,存在某個表中但永遠不會釋放),那么主進程中的對應對象也一樣會泄露,所以你必須小心不要泄露了遠程對象。
但是,字符串和數字等主要值的類型是通過復制發送的。
3.3給主進程傳遞回調函數
在主進程中的代碼可以從渲染進程——remote模塊——中接受回調函數,但是使用這個功能的時候必須非常非常小心。
首先,為了避免死鎖,傳遞給主進程的回調函數會進行異步調用。所以不能期望主進程來獲得傳遞過去的回調函數的返回值。
其次,傳遞給主進程的回調將持續到主進程垃圾回收。主進程會一直保持對這個回調函數的引用,除非明確的卸載它。如果不卸載,每次重新載入窗口都會再次綁定,這樣每次重啟就會泄露一個回調函數。為了避免這個問題,請確保清除對傳遞給主進程的渲染器回調的引用。 這涉及到清理事件處理程序, 或者確保主進程被明確告知取消引用來自正在退出的渲染程序的回調。
3.4訪問主進程中的內置模塊
主過程中的內置模塊被添加為 remote 模塊中的獲取器,因此可以像 electron 模塊一樣直接使用它們
const app = require('electron').remote.app
console.log(app)
3.5remote的幾個方法及屬性
返回主進程中require(module) 返回的對象。 由其相對路徑指定的模塊將相對于主進程的入口點來解析。
返回 BrowserWindow
即此網頁所屬的窗口
返回 WebContents
即此網頁的 web 內容
返回主進程中 name (例如 global[name]) 的全局變量。
主進程中的 process 對象。這與 remote.getGlobal('process') 相同, 但已被緩存。