一、主進程和渲染進程
1.1 主進程和渲染進程的概念
在上手ipc通信之前,得對electron的進程模型有個基本的了解,electron中的進程大致可以分為主進程和渲染進程,主進程指的就是nodejs的進程,而渲染進程則可以簡單的理解為一個Chromium的web頁面。主進程,也就是nodejs進程只有一個,而渲染進程,也就是Chromium的web頁面則可以有多個,ipc通信就是在主進程和渲染進程之間通信。
1.2 主進程和渲染進程的環境區別
認為主進程的代碼是開發者自己寫的,就是普通的node程序,但是渲染進程有可能會加載網頁之類的,可能會執行第三方的js代碼,渲染進程的權限是被限制了的,所以某些敏感操作就需要渲染進程給主進程發消息,讓主進程去做,然后完事了再通知渲染進程。
在使用electron開發客戶端程序的時候,將其類比為web端的B/S架構,其渲染進程就是前端的頁面,是門面,而主進程則是服務器后台,負責處理一些與UI無關的業務邏輯,主進程和渲染進程不可避免的要進行一些數據、指令的交換,即這兩個進程之間要通信交換數據,這就是進程間通信,electron提供了ipc模塊用於在主進程和渲染進程之間通信。
二、主進程與渲染進程通信
2.1 渲染進程向主進程發送異步消息
渲染進程向主進程發送消息默認就是異步的,在渲染進程的html或者render.js中向主進程發送異步消息:
const electron = require("electron");
// 第一個參數是約定的事件的名稱,主進程要監聽這個事件
// 第二個及之后的參數是要發送的具體的消息
electron.ipcRenderer.send("render-to-main-message", message);
send方法至少需要兩個參數,第一個是事件名稱,在主線程中需要監聽此事件,然后是至少一個參數的事件參數,可以有多個,但是至少有一個。
在主線程main.js中接收事件:
// 接收從渲染進程發送到主進程的消息
ipcMain.on("render-to-main-message", (event, message) => {
// 在控制台上打印一下
console.log(`receive message from render: ${message}`);
// 回復消息
event.reply("main-to-render-message", `來自主進程:我收到了你的消息“${message}”`);
})
使用 event.reply 在主進程中回復渲染進程發過來的消息,因為這個replay指定了一個事件名稱,因此在渲染進程render.js中也要監聽事件才能收到回復:
// 接收主進程發來的消息
// 第一個參數是約定的事件的名稱
// 第二個參數是處理事件的callback
electron.ipcRenderer.on("main-to-render-message", (event, message) => {
alert(message);
});
2.2 渲染進程向主進程發送同步消息
同步消息是渲染進程向主進程發送的,因為渲染進程頁面可以有多個,而主進程node進程只有一個,因此渲染進程可以等主進程,而主進程不能同步等渲染進程。
渲染進程向主進程發送同步消息,使用 electron.ipcRenderer.sendSync 方法,這個方法的返回值是從主進程中返回的,在渲染進程render.js中向主進程發送消息:
const electron = require("electron");
// 發送同步消息
const replayMessage = electron.ipcRenderer.sendSync("render-to-main-message-sync", message);
alert(replayMessage);
在主進程main.js中接收並回復渲染進程發過來的同步消息:
// 接收同步消息
ipcMain.on("render-to-main-message-sync", (event, message) => {
// 控制台打印一下知道來了
console.log(`receive sync message from render: ${message}`);
// 回復渲染進程的同步消息
event.returnValue = `來自主進程:我收到了你的消息“${message}”`;
})
在主進程中對於同步消息和異步消息的接收沒有區別,只是回復同步消息的時候變成了:
event.returnValue = "要回復的消息內容"
2.3 主進程向渲染進程發送消息
渲染進程可以向主進程發送同步消息或者異步消息,但是主進程只能向渲染進程發送異步消息,比如下面是在主進程啟動后3秒向渲染進程發送一條消息:
function createWindow() {
const window = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// 注意這個路徑是從項目根路徑開始的
window.loadFile("example/use-ipc/index.html")
setTimeout(() => {
window.webContents.send("main-to-render-message", "啟動完成了");
}, 3000)
}
在渲染進程render.js中監聽此事件:
const electron = require("electron");
electron.ipcRenderer.on("main-to-render-message", (event, message) => {
alert(message);
});
2.4 代碼及運行效果一覽
上面的三種發送方式都是只提取了部分代碼做展示,完整的例子代碼如下:
main.js:
const {app, BrowserWindow, ipcMain} = require("electron")
function createWindow() {
const window = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// 注意這個路徑是從項目根路徑開始的
window.loadFile("example/use-ipc/index.html")
setTimeout(() => {
window.webContents.send("main-to-render-message", "啟動完成了");
}, 3000)
}
app.whenReady().then(createWindow)
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit()
}
})
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
// 接收從渲染進程發送到主進程的消息
ipcMain.on("render-to-main-message", (event, message) => {
// 在控制台上打印一下
console.log(`receive message from render: ${message}`);
// 回復消息
event.reply("main-to-render-message", `來自主進程:我收到了你的消息“${message}”`);
})
// 接收同步消息
ipcMain.on("render-to-main-message-sync", (event, message) => {
// 控制台打印一下知道來了
console.log(`receive sync message from render: ${message}`);
// 回復渲染進程的同步消息
event.returnValue = `來自主進程:我收到了你的消息“${message}”`;
})
index.html文件(render.js沒有單獨提出來,render的js直接放到html中了):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IPC通信</title>
</head>
<body>
<h1>IPC通信</h1>
<input type="text" id="message">
<button onclick="sendMessageToMainProcess()">發送消息給主進程</button>
<script type="text/javascript">
const electron = require("electron");
// 接收主進程發來的消息
// 第一個參數是約定的事件的名稱
// 第二個參數是處理事件的callback
electron.ipcRenderer.on("main-to-render-message", (event, message) => {
alert(message);
});
// 向主進程發送消息
function sendMessageToMainProcess() {
const message = document.getElementById("message").value;
// 第一個參數是約定的事件的名稱,主進程要監聽這個事件
// 第二個及之后的參數是要發送的具體的消息
electron.ipcRenderer.send("render-to-main-message", message);
// 發送同步消息
const replayMessage = electron.ipcRenderer.sendSync("render-to-main-message-sync", message);
alert(replayMessage);
}
</script>
</body>
</html>
效果:

本文所用Electron版本:"electron": "^11.1.0"
