进程通信
由于 preload.js
并无法使用全部 Node 的API ,比如:不能使用 Node 中的 fs
模块,但主进程(main.js)是可以的,这时就需要进程通信了。简单说:要让 preload.js
通知 main.js
去调用 fs
模块。
关于Electron进程通信,我们要知道:
-
IPC
全称为:InterProcess Communication
,即:进程通信。
-
IPC
是 Electron
中最为核心的内容,它是从 UI
调用原生 API
的唯一方法!
-
Electron
中,主要使用 ipcMain
和 ipcRenderer
来定义“通道”,进行进程通信。
渲染进程=>主进程(单向)
在渲染器进程中 ipcRenderer.send
发送消息,在主进程中使用 ipcMain.on
接收消息。
常用于:在 Web 中调用主进程的 API
(1)页面中添加相关元素,renderer.js 中添加对应脚本
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta
6 httpEquiv="Content-Security-Policy"
7 content="default-src 'self'; script-src 'self'"
8 />
9 <title>Hello World!</title>
10 </head>
11
12 <body>
13 <input
14 id="content"
15 type="text"
16 />
17 <button id="btn">在用户的桌面创建一个hello.txt</button>
18 <script
19 type="text/javascript"
20 src="./renderer.js"
21 ></script>
22 </body>
23</html>
1const btn = document.getElementById("btn")
2const content = document.getElementById("content")
3btn.addEventListener("click", () => {
4 console.log(content.value)
5 myAPI.saveFile(content.value)
6})
:::
(2)preload.js 中使用 ipcRenderer.send('信道',参数)
发送消息,与主进程通信。
1const { contextBridge, ipcRenderer } = require("electron")
2
3// 暴露数据给渲染进程
4contextBridge.exposeInMainWorld("myAPI", {
5 name: "dancy",
6 age: 28,
7 saveFile(str) {
8 // 渲染进程给主进程发送一个消息
9 ipcRenderer.send("create-file", str)
10 },
11})
(3)主进程中,在加载页面之前,使用 ipcMain.on('信道',回调)
配置对应回调函数,接收消息。
1const { app, BrowserWindow, ipcMain } = require("electron")
2const fs = require("node:fs")
3const path = require("node:path")
4
5// 获取桌面路径
6const desktopPath = path.join(process.env.HOME, "Desktop")
7// 要写入的文件名
8const fileName = "hello.txt"
9// 使用writeFileSync方法创建并写入文件
10function createFile(event, data) {
11 fs.writeFileSync(path.join(desktopPath, fileName), data)
12}
13
14const createWindow = () => {
15 const win = new BrowserWindow({
16 width: 300,
17 height: 200,
18 autoHideMenuBar: true,
19 webPreferences: {
20 preload: path.resolve(__dirname, "./preload.js"),
21 },
22 })
23
24 // 主进程注册对应回调
25 ipcMain.on("create-file", createFile)
26
27 win.loadFile("./pages/index.html")
28}
29
30app.on("ready", () => {
31 createWindow()
32 app.on("activate", () => {
33 if (BrowserWindow.getAllWindows().length === 0) createWindow()
34 })
35})
36
37app.on("window-all-closed", () => {
38 if (process.platform !== "darwin") app.quit()
39})
渲染进程<=>主进程(双向)
渲染进程通过 ipcRenderer.invoke
发送消息,主进程使用 ipcMain.handle
接收并处理消息。
备注:ipcRender.invoke
的返回值是 Promise
实例。
常用于:从渲染器进程调用主进程方法并等待结果
(1)页面中添加相关元素,renderer.js中添加对应脚本
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta
6 httpEquiv="Content-Security-Policy"
7 content="default-src 'self'; script-src 'self'"
8 />
9 <title>Hello World!</title>
10 </head>
11
12 <body>
13 <button id="btn">读取用户桌面hello.txt的内容</button>
14 <script
15 type="text/javascript"
16 src="./renderer.js"
17 ></script>
18 </body>
19</html>
1const btn = document.getElementById("btn")
2btn.addEventListener("click", async () => {
3 const data = await myAPI.readFile()
4 document.body.innerHTML += `<h2>${data}</h2>`
5})
:::
(2)preload.js中使用 ipcRenderer.invoke('信道',参数)
发送消息,与主进程通信。
1const { contextBridge, ipcRenderer } = require("electron")
2
3// 暴露数据给渲染进程
4contextBridge.exposeInMainWorld("myAPI", {
5 name: "dancy",
6 age: 28,
7 saveFile(str) {
8 // 渲染进程给主进程发送一个消息
9 ipcRenderer.send("create-file", str)
10 },
11 readFile() {
12 return ipcRenderer.invoke("read-file")
13 },
14})
(3)主进程中,在加载页面之前,使用 ipcMain.handle('信道',回调)
接收消息,并配置回调函数。
1const { app, BrowserWindow, ipcMain } = require("electron")
2const fs = require("node:fs")
3const path = require("node:path")
4
5// 获取桌面路径
6const desktopPath = path.join(process.env.HOME, "Desktop")
7// 要写入的文件名
8const fileName = "hello.txt"
9// 使用writeFileSync方法创建并写入文件
10function createFile(event, data) {
11 fs.writeFileSync(path.join(desktopPath, fileName), data)
12}
13//读取文件
14function readFile() {
15 return fs.readFileSync(path.join(desktopPath, fileName)).toString()
16}
17
18const createWindow = () => {
19 const win = new BrowserWindow({
20 width: 300,
21 height: 200,
22 autoHideMenuBar: true,
23 webPreferences: {
24 preload: path.resolve(__dirname, "./preload.js"),
25 },
26 })
27
28 ipcMain.on("create-file", createFile)
29
30 // 主进程注册对应回调
31 ipcMain.handle("read-file", readFile)
32
33 win.loadFile("./pages/index.html")
34}
35
36app.on("ready", () => {
37 createWindow()
38 app.on("activate", () => {
39 if (BrowserWindow.getAllWindows().length === 0) createWindow()
40 })
41})
42
43app.on("window-all-closed", () => {
44 if (process.platform !== "darwin") app.quit()
45})
主进程到=>渲染进程
概述:主进程使用 win.webContents.send
发送消息,渲染进程通过 ipcRenderer.on
处理消息
常用于: 从主进程主动发消息给渲染进程
(1)页面中添加相关元素, renderer.js 中添加对应脚本
1window.onload = () => {
2 myAPI.getMessage(logMessage)
3}
4function logMessage(event, str) {
5 console.log(event, str)
6 document.body.innerHTML = str
7}
(2)preload.js中使用 ipcRenderer.on ('信道',回调)
接收消息,并配置回调函数。
1const { contextBridge, ipcRenderer } = require("electron")
2
3// 暴露数据给渲染进程
4contextBridge.exposeInMainWorld("myAPI", {
5 name: "dancy",
6 age: 28,
7 saveFile(str) {
8 // 渲染进程给主进程发送一个消息
9 ipcRenderer.send("create-file", str)
10 },
11 readFile() {
12 return ipcRenderer.invoke("read-file")
13 },
14 getMessage: (callback) => {
15 return ipcRenderer.on("message", callback)
16 },
17})
(3)主进程中,在合适的时候,使用 win.webContents.send('信道',数据)
发送消息。
1const { app, BrowserWindow, ipcMain } = require("electron")
2const fs = require("node:fs")
3const path = require("node:path")
4
5// 获取桌面路径
6const desktopPath = path.join(process.env.HOME, "Desktop")
7// 要写入的文件名
8const fileName = "hello.txt"
9// 使用writeFileSync方法创建并写入文件
10function createFile(event, data) {
11 fs.writeFileSync(path.join(desktopPath, fileName), data)
12}
13//读取文件
14function readFile() {
15 return fs.readFileSync(path.join(desktopPath, fileName)).toString()
16}
17
18// 创建窗口
19const createWindow = () => {
20 const win = new BrowserWindow({
21 width: 300,
22 height: 200,
23 autoHideMenuBar: true,
24 webPreferences: {
25 preload: path.resolve(__dirname, "./preload.js"),
26 },
27 })
28
29 // 主进程注册对应回调
30 ipcMain.on("create-file", createFile)
31
32 // 主进程注册对应回调
33 ipcMain.handle("read-file", readFile)
34
35 // 加载指定的文件到新创建的窗口中,这个文件通常是应用的主页面。
36 win.loadFile("./pages/index.html")
37
38 // 创建一个定时器
39 setTimeout(() => {
40 win.webContents.send("message", "你好啊!")
41 }, 2000)
42}
43
44app.on("ready", () => {
45 createWindow()
46 app.on("activate", () => {
47 if (BrowserWindow.getAllWindows().length === 0) createWindow()
48 })
49})
50
51
52app.on("window-all-closed", () => {
53 if (process.platform !== "darwin") app.quit()
54})