目录
主进程和渲染进程
在electron中,GUI的相关的模块仅在主进程中可用
若渲染进程要完成GUI操作,有两种方式:
- 渲染进程向主进程发送消息,让主进程完成相应的操作
- 通过渲染进程的remote模块完成相应的操作
调试主进程
在VSC中,点击左侧的菜单栏的 debug图标,添加launch.json
, 添加配置Node.js Electron 主程序
自动生成如下配置:
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Electron Main",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"program": "${workspaceFolder}/main.js",
"skipFiles": [
"<node_internals>/**"
]
}
]
}
在主进程`main.js`中打个断点, 按下快捷键 F5,即可进行调试
调试渲染进程
使用Chrome浏览器的开发者工具, 快捷方式 Ctrl + Shift + I
进程互访
remote
渲染进行访问主进程对象
remote`对象的属性和方法(包括类型的构造函数) 都是主进程的属性和方法的映射。
index.html
<script>
require('./index.js')
</script>
index.js
let { remote } = require('electron');
remote.getCurrentWindow().webContents.openDevTools();
渲染进程访问主进程自定义内容
-
创建一个Model
mainModel.js
let { BrowserWindow } = require('electron') exports.makeWin = function () { let win = new BrowserWindow({ webPreferences: { nodeIntegration: true } }); return win; }
-
index.html
和index.js
<!DOCTYPE html> <html> <head> <title>窗口标题</title> </head> <body> <h1>Hello World!</h1> <div> <button id="btn-make-win">创建窗口</button> </div> <script> require('./index.js') </script> </body> </html>
let { remote } = require('electron'); let mainModel = remote.require('./mainModel'); remote.getCurrentWindow().webContents.openDevTools(); let win2 = null; document.getElementById("btn-make-win").addEventListener('click', () => { win2 = mainModel.makeWin(); win2.loadFile('index.html'); })
使用
remote.require
加载mainModel.js'
,makeWin
将在主进程中运行。主进程访问渲染进程对象
因为渲染进程是有主进程创建的,故主进程可以以直接访问渲染进程的对象和类型
进程间消息传递
渲染进程向主进程发送消息
- index.html
<button id="btn-ipc-renderer">渲染进程向主进程发送消息</button>
-
index.js
let { ipcRenderer } = require('electron'); document.getElementById("btn-ipc-renderer").addEventListener('click', () => { ipcRenderer.send('msg_renderer_to_main', { 'param1': "hello" }, { 'param2': "world" }); })
渲染进程使用
ipcRenderer.send()` 发送消息 -
main.js
let { ipcMain} = require('electron'); ... app.on('ready', () => { ... ipcMain.on("msg_renderer_to_main", (event, param1, param2) => { console.log(param1); console.log(param2); console.log(event.sender); }) });
主机进程使用
ipcMain.on
接收消息
主进程向渲染进程发送消息
-
main.js
ipcMain.on("msg_renderer_to_main", (event, param1, param2) => { console.log(param1); console.log(param2); console.log(event.sender); //主进程向渲染进程发送消息:方式一 event.sender.send("msg_main_to_renderer_bysend", { "arg1": "send" }, "msg from main by send!"); //主进程向渲染进程发送消息:方式二 event.reply("msg_main_to_renderer_byreply", { "reply-arg1": "reply" }, "msg from main by reply!"); //主进程向渲染进程发送消息:方式三 win.send("msg_main_to_renderer_byWindow", { "Window-arg1": "reply" }, "msg from main by Window!"); })
主线程使用`event.sender.send()`、`event.reply()`、`BrowseWindow.send()` 向渲染进程发送消息,
其中 ,event.sender
:代表发送消息的渲染进程的webContents
特别注意
无论使用event.sender.send
还是event.reply
,
只有event
的发送者所在的渲染进程才收到消息, 如果使用win.send()
,那么 只用win所在的在渲染进程才能收到消息
- index.js
ipcRenderer.on('msg_main_to_renderer_bysend', (event, param1, param2) => {
console.log(param1);
console.log(param2);
});
ipcRenderer.on('msg_main_to_renderer_byreply', (event, param1, param2) => {
console.log(param1);
console.log(param2);
});
ipcRenderer.on('msg_main_to_renderer_byWindow', (event, param1, param2) => {
console.log(param1);
console.log(param2);
});
渲染进程使用ipcRenderer.on()
接收主线程的消息
渲染进程间消息传递
通过主进程中转
窗口之间传递的消息,都先发到主进程,再由主进程转发
通过窗口的webContents.id
-
index.html
<button id="btn-send-msg-between-renderer">渲染进程间发送消息</button>
-
index.js
let win2 = null; document.getElementById("btn-make-win").addEventListener('click', () => { win2 = mainModel.makeWin(); win2.loadFile('index.html'); }) ... //渲染进程间直接发送消息 document.getElementById("btn-send-msg-between-renderer").addEventListener('click', () => { console.log("msg_between_renderer"); ipcRenderer.sendTo(win2.webContents.id, 'msg_renderer_to_renderer', { 'param1': "hello" }, { 'param2': "world" }); }); //接收渲染进程间消息 ipcRenderer.on('msg_renderer_to_renderer', (event, param1, param2) => { console.log(param1); console.log(param2); });
Remote模块的局限性
-
性能消耗大
-
制造混乱
-
制造假象
-
存在安全问题