目录
一、主进程和渲染进程
1.主进程:是electron应用的大脑负责管理整个应用的生命周期,主要负责:
创建和管理应用窗口
处理系统级别的操作(如文件读写、菜单和任务栏等)
与操作系统直接交互
特点:每个electron应用只有一个主进程并且其运行在node.js环境中可以使用node.js的所有功能
2.渲染进程:是electron的手和脚,负责显示和页面操作,主要负责:
每个窗口都有一个独立的渲染进程
负责加载和显示网页的内容以及处理用户的交互
特点:每个窗口的渲染进程都是独立的且互不影响
3.主进程和渲染进程的关系:
协作:主进程可以创建多个窗口,每个窗口对应一个渲染进程
渲染进程可以请求主进程执行系统级别的任务
主进程可以将任务结果返回给渲染进程并更新界面
二、主进程和渲染进程的通信
1.通信的核心模块:
ipcMain(其中IPC为进程间通信):主进程来使用用于监听渲染进程发送的消息
ipcRenderer:渲染进程使用用来主进程发送消息并接收回复
2.通信的基本流程:
渲染进程发送消息:渲染进程通过ipcRenderer.send向主进程发送消息
主进程监听消息:主进程通过ipcMain.on监听渲染进程发送的消息
主进程处理消息:主进程执行相关操作(读取文件或者访问数据库等操作)
主进程回复消息:主进程通过event.reply将结果发送回渲染进程
渲染进程接收回复:渲染进程通过ipcRenderer.on接收主进程的回复并更新界面
3.代码示例(渲染进程发送文件路径给主进程,主进程读取文件内容并返回给渲染进程):
const { app, BrowserWindow, ipcMain } = require('electron');
const { readFileSync } = require('fs');
const path = require('path');
let mainWindow;
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 预加载脚本
contextIsolation: true, // 启用上下文隔离
},
});
mainWindow.loadFile('index.html');
});
// 监听渲染进程发送的 "read-file" 消息
ipcMain.on('read-file', (event, filePath) => {
try {
const content = readFileSync(filePath, 'utf-8');
// 将文件内容发送回渲染进程
event.reply('file-content', content);
} catch (error) {
event.reply('file-content', `Error: ${error.message}`);
}
});
代码解读:前三行代码的作用是导入必要的模块,其中app是用来控制应用的生命周期,BrowserWindow是用来创建和管理应用程序窗口,readFileSync是node.js中的文件读取方法,允许同步的读取文件内容,path是用来处理和转换文件路径的node.js模块。
第二部分代码是创建一个新的窗口,对窗口进行初始化并预加载preload脚本并启用上下文隔离防止渲染进程直接访问node.js环境,这些操作完成之后会触发ready事件。最后加载本地的Index.html文件作为窗口的内容
第三部分代码是监听渲染进程发送的read-file消息,其中event为事件对象用于回复消息,filePath为渲染进程传递过来的文件路径。最后做的操作就是同步读取指定路径的文件内容并指定其编码格式为utf-8然后将读取到的文件内容通过file-content事件发送回渲染进程
加载脚本和渲染进程代码:
const { contextBridge, ipcRenderer } = require('electron');
// 将 ipcRenderer 的方法暴露给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => ipcRenderer.send(channel, data),
on: (channel, callback) => ipcRenderer.on(channel, callback),
});
// 通过预加载脚本暴露的 API 与主进程通信
window.electronAPI.on('file-content', (event, content) => {
document.getElementById('file-content').innerText = content;
});
document.getElementById('read-file').addEventListener('click', () => {
const filePath = '/path/to/your/file.txt';
// 向主进程发送 "read-file" 消息
window.electronAPI.send('read-file', filePath);
});
代码解读:第一块代码中,分别导入两个模块,第一个模块是用于在渲染进程和主进程之间安全的暴露api,第二个模块就是用于渲染进程和主进程之间通信的模块。接下来的代码是分别封装了向主进程发送消息和监听主进程发送的消息并将这两个方法暴露到渲染进程的全局对象。
第二块代码中,主要是用来调用上段代码暴露的方法来向主进程发送消息,并时刻监听主进程发送过来的消息内容。
index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>Electron IPC Example</title>
</head>
<body>
<h1>File Content:</h1>
<pre id="file-content">No content yet.</pre>
<button id="read-file">Read File</button>
<script src="renderer.js"></script>
</body>
</html>
三、窗口管理
1.窗口的基本概念:每个窗口都是一个独立的浏览器窗口,由主进程通过BrowserWindow模块创建和管理,每个窗口对应一个渲染进程负责加载和显示网页内容。
2.创建窗口:
// 主进程代码(main.js)
const { app, BrowserWindow } = require('electron');
let mainWindow;
app.whenReady().then(() => {
// 创建主窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true, // 是否启用 Node.js 集成(默认禁用)
contextIsolation: true, // 启用上下文隔离(推荐)
preload: './preload.js', // 预加载脚本
},
});
// 加载页面
mainWindow.loadFile('index.html');
// 窗口关闭时释放引用
mainWindow.on('closed', () => {
mainWindow = null;
});
});
代码解读:
width/height:窗口的初始尺寸
min(max)Width/min(max)Height:窗口的最小(最大)的尺寸
title:窗口的标题
frame:是否显示窗口的边框(默认为true,false表示创建无边框的窗口)
transparent:窗口是否透明(需要结合frame:false来使用)
resizeable:窗口的是否可以调整大小
alwaysOnTop:窗口是否指定
webPreferences:网页配置
3.多窗口管理:
// 主进程代码(main.js)
let settingsWindow;
function createSettingsWindow() {
settingsWindow = new BrowserWindow({
width: 400,
height: 300,
parent: mainWindow, // 设置父窗口(可选)
modal: true, // 设置为模态窗口(阻塞父窗口交互)
});
settingsWindow.loadFile('settings.html');
settingsWindow.on('closed', () => {
settingsWindow = null;
});
}
// 在某个事件(如点击菜单)中调用
app.on('some-event', () => {
if (!settingsWindow) {
createSettingsWindow();
}
});
代码解读:当触发some-event事件时主进程会检查设置窗口是否已经存在,如果不存在主进程会创建新的设置窗口,创建窗口时会加载setting.html文件并显示在父窗口之上
4.窗口事件:
常用事件:
ready-to-show:窗口内容加载完成可以显示
close:窗口即将关闭
closed:窗口已关闭
maxmize/unmaximize:窗口最大化或者取消最大化
minimize/restore:窗口最小化或还原
代码示例(阻止窗口关闭):
mainWindow.on('close', (event) => {
// 如果用户有未保存的数据,阻止关闭
if (hasUnsavedData) {
event.preventDefault(); // 阻止关闭
dialog.showMessageBox({
type: 'warning',
buttons: ['保存并退出', '不保存退出', '取消'],
message: '您有未保存的更改,是否保存?'
}).then((result) => {
if (result.response === 0) {
saveData().then(() => mainWindow.destroy());
} else if (result.response === 1) {
mainWindow.destroy();
}
});
}
});
代码解读:该段代码首先创建了一个事件监听器用于捕获窗口的close事件,当用户尝试关闭窗口时这个事件会触发,事件对象event提供了控制窗口关闭的功能。之后如果用户选择了保存并退出会调用saveDate()保存数据,保存成功会销毁窗口并关闭应用,如果用户选择不保存退出直接调用窗口销毁函数并关闭应用。
5.父子窗口与模态窗口:
父子窗口:
父窗口:通过parent属性指定父窗口,子窗口会始终显示在父窗口的上方
模态窗口:通过modal:true设置,子窗口会阻塞父窗口的交互
const childWindow = new BrowserWindow({
parent: mainWindow,
modal: true,
width: 400,
height: 300,
});
代码解读:这段代码创建了一个400*300的子窗口,并且设置它为模态窗口且它的父窗口是mainwindow。用户在这个子窗口时,无法与父窗口进行任何操作。
6.窗口之间通信:
不同窗口的渲染进程之间不能直接通信,必须通过主进程进行消息转发
方法1:通过主进程转发
// 主进程(main.js)
ipcMain.on('send-to-settings', (event, data) => {
if (settingsWindow) {
settingsWindow.webContents.send('receive-data', data);
}
});
代码解读:当主进程接收到名为‘send-to-settings’的消息时,先检查是否有一个名为settingsWindow的设置窗口,如果窗口存在,主进程会通过webContents.send接收到的数据发送到设置窗口,设置窗口通过监听receive-data事件来接收数据并处理
方法2:直接使用webContents
// 主进程(main.js)
mainWindow.webContents.send('update-data', { value: 42 });
7.窗口的常用方法:
显示与隐藏:
mainWindow.show(); // 显示窗口
mainWindow.hide(); // 隐藏窗口
最大化与最小化:
mainWindow.maximize(); // 最大化
mainWindow.unmaximize();
mainWindow.minimize();
mainWindow.restore(); // 从最小化还原
聚焦窗口:
mainWindow.focus(); // 将窗口置于最前方
四、Electron生命周期
1.electron的生命周期由app模块管理,它提供了多个事件来监听应用的不同状态
生命周期的关键阶段:
启动:应用初始化
就绪:应用准备好创建窗口
运行:窗口创建并显示用户与应用交互
退出:应用关闭并释放资源
2.核心事件:
ready:electron完成初始化准备创建窗口
在此事件中创建主窗口
app.on('ready', () => {
createMainWindow();
});
window-all-closed:所有窗口都已经关闭
在此事件中决定应用是否退出
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
before-quit:应用即将退出
在此事件中执行清理操作(保存数据)
app.on('before-quit', (event) => {
if (hasUnsavedData) {
event.preventDefault(); // 阻止退出
saveData().then(() => app.quit());
}
});
will-quit:应用即将退出,所有窗口已关闭
执行最后的清理操作
app.on('will-quit', () => {
console.log('应用即将退出');
});
quit:应用已退出
记录日志或执行最终操作
app.on('quit', () => {
console.log('应用已退出');
});