1.主进程
核心:使用BrowserWindow来创建和管理窗口。
每个Electron应用都有且仅有一个主进程,它主要在Node.js环境当中运行,具有require模块和使用所有Node.js API(如__dirname、fs、process等)的能力,无法访问window或alert等这些非Node.js的API。
主进程通过 app
模块控制应用的生命周期,并通过 BrowserWindow
创建窗口。例如:
const { app, BrowserWindow } = require('electron');
app.whenReady().then(() => {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
});
2.渲染进程
核心:将HTML、CSS和JavaScript转换为可交互的网页(包括解析HTML、构建DOM树、样式计算、子资源加载等 );
每个BrowserWindow实例都对应一个单独的渲染器进程,主要运行在 Chromium 环境中,它可以访问window、alert等API,但无法使用任何Node.js的API,也不具有require模块的能力。
一个主进程可同时管理多个渲染进程。
默认情况下,渲染进程无法调用任何Node.js的API,但如果需要,可以通过预加载脚本来安全地访问Node.js的部分API(像__dirname、fs就无法被访问)。
3.预加载脚本(preload)
作用:Node.js和Web API之间的桥梁,它可以安全地将部分Node.js API暴露给网页。
实现:主进程在 new BrowserWindow 内增加属性 webPreferences ,并用绝对路径引入preload.js文件,preload.js文件中通过 electron 的 contextBridge.exposeInMainWorld 将部分API安全地暴露给渲染进程,在渲染进程中的window即可访问到这些被暴露的API。
完整代码:
main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800, // 窗口宽度
height: 600, // 窗口高度
autoHideMenuBar: true, // 隐藏默认的菜单栏
// x: 0, // 窗口在横坐标上的位置
// y: 0, // 窗口在纵坐标上的位置
// alwaysOnTop: true, // 窗口置顶
webPreferences: {
preload: path.resolve(__dirname, './preload.js') // 必须写绝对路径
} // web高级配置
});
win.loadFile('./pages/index.html')
}
app.on('ready', () => {
console.log('启动了应用!')
createWindow();
// (macOS专用)当应用被激活时,判断当前是否有窗口,若没有,则创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
});
// 当所有窗口关闭,并且当前不是macOS系统时,退出应用
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
});
preload.js
console.log('preload', process.version);
const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('myAPI', {
version: process.version
}); // 接收两个参数,第一个参数是key,render.js中用该key值访问,第二个参数则是值
render.js
const btn1 = document.getElementById('btn1');
btn1.onclick = () => {
console.log('version', myAPI.version);
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>index</title>
<link rel="stylesheet" href="./index.css">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"/>
</head>
<body>
<h1>Electron开发</h1>
<button id="btn1">点我一下</button>
<script type="text/javascript" src="./render.js" ></script>
</body>
</html>
如此即可完成点击按钮【点我一下】就输出node版本这一小功能。
预加载脚本运行在渲染进程当中,但在网页内容加载之前执行,它比普通渲染器代码有更高的权限,可以访问Node.js的API,同时又可以与网页内容进行安全的交互。
若现有主进程(main.js)、预加载脚本(preload.js)、渲染进程(render.js)三个脚本,且三个脚本中各有一个console输出,则正确的输出顺序是main.js —> preload.js —> render.js,其中,preload.js和render.js的console输出都在BrowserWindow窗口的控制台中。
4.进程通信(IPC)
IPC:InterProcess Communication,是UI调用原生API的唯一方法。
为什么需要IPC?
预加载脚本无法使用__dirname、fs等Node.js API,当渲染进程需要用到这些API时,只能通过IPC来通知主进程调用这些模块来干活。
4.1 渲染进程 —>主进程(单向)
预加载脚本:ipcRenderer.send('信道', 参数) 发送;
主进程:ipc.on('信道', 回调) 接收;
4.2 渲染进程 <—>主进程(双向)
预加载脚本:ipcRenderer.invoke('信道', 参数) 调用(invoke的返回值是一个promise实例);
主进程:ipc.handle('信道', 回调) 处理;
4.3 主进程 —>渲染进程(单向)
主进程:win.webContents.send('信道', 数据) 发送;
预加载脚本:ipcRenderer.on('信道', 回调) 接收并调用回调函数;
4.4 渲染进程 <—>渲染进程
渲染进程与渲染进程之间无法直接进行通信,可以将主进程作为渲染器之间的消息代理(中介)。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
例:实现将窗口1中input的值复制到窗口2中展示的小功能
最终结果展示: