第2章:主进程与渲染进程
深入理解 Electron 的双进程架构,掌握主进程和渲染进程的职责分工
2.1 Electron 进程模型概述
Electron 采用多进程架构,这种设计借鉴了现代浏览器的架构模式,提供了更好的稳定性和安全性。
为什么需要多进程架构?
- 稳定性:一个进程崩溃不会影响其他进程
- 安全性:渲染进程运行在沙箱环境中
- 性能:可以充分利用多核 CPU
- 隔离性:不同窗口之间相互独立
进程架构图
┌─────────────────────────────────────────────────────────┐
│ Electron 应用 │
├─────────────────────────────────────────────────────────┤
│ 主进程 (Main Process) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 应用生命周期管理 │ │
│ │ • 创建和管理 BrowserWindow │ │
│ │ • 处理系统事件 │ │
│ │ • 原生 API 访问 │ │
│ │ • IPC 通信中心 │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 渲染进程 (Renderer Process) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────┐ │
│ │ 窗口 1 │ │ 窗口 2 │ │ ... │ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ │ │
│ │ │ HTML/CSS/JS │ │ │ │ HTML/CSS/JS │ │ │ │ │
│ │ │ Web APIs │ │ │ │ Web APIs │ │ │ │ │
│ │ │ 部分Node.js │ │ │ │ 部分Node.js │ │ │ │ │
│ │ └─────────────┘ │ │ └─────────────┘ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
2.2 主进程 (Main Process)
主进程是 Electron 应用的核心,负责整个应用的生命周期管理和系统级操作。
主进程的职责
应用生命周期管理
- 应用启动和退出
- 处理应用级事件
- 管理应用状态
窗口管理
- 创建和销毁 BrowserWindow
- 控制窗口属性和行为
- 管理多窗口应用
系统集成
- 访问原生 API
- 处理系统通知
- 管理菜单和托盘
进程间通信
- 作为 IPC 通信的中心
- 协调不同渲染进程
主进程示例代码
创建 main-process-demo.js
:
javascript
const { app, BrowserWindow, ipcMain, Menu, dialog } = require('electron')
const path = require('path')
class MainProcess {
constructor() {
this.windows = new Set()
this.setupEventHandlers()
}
setupEventHandlers() {
// 应用准备就绪
app.whenReady().then(() => {
this.createMainWindow()
this.setupMenu()
this.setupIPC()
})
// 所有窗口关闭
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// 应用激活 (macOS)
app.on('activate', () => {
if (this.windows.size === 0) {
this.createMainWindow()
}
})
// 应用即将退出
app.on('before-quit', (event) => {
console.log('应用即将退出')
// 可以在这里保存应用状态
})
}
createMainWindow() {
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
// 窗口关闭时从集合中移除
mainWindow.on('closed', () => {
this.windows.delete(mainWindow)
})
this.windows.add(mainWindow)
return mainWindow
}
setupMenu() {
const template = [
{
label: '文件',
submenu: [
{
label: '新建窗口',
accelerator: 'CmdOrCtrl+N',
click: () => this.createMainWindow()
},
{ type: 'separator' },
{
label: '退出',
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
click: () => app.quit()
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
setupIPC() {
// 处理来自渲染进程的消息
ipcMain.handle('get-app-info', () => {
return {
name: app.getName(),
version: app.getVersion(),
platform: process.platform,
arch: process.arch
}
})
ipcMain.handle('show-message-box', async (event, options) => {
const result = await dialog.showMessageBox(options)
return result
})
}
}
// 创建主进程实例
new MainProcess()
2.3 渲染进程 (Renderer Process)
渲染进程负责显示用户界面,每个 BrowserWindow 都对应一个独立的渲染进程。
渲染进程的特点
基于 Chromium
- 支持最新的 Web 标准
- 强大的 JavaScript 引擎
- 完整的 DOM API
沙箱环境
- 默认运行在安全沙箱中
- 限制对系统资源的直接访问
- 通过 IPC 与主进程通信
Web 技术栈
- HTML/CSS/JavaScript
- 现代前端框架支持
- 丰富的 Web API
渲染进程安全配置
javascript
// 安全的 webPreferences 配置
const secureWebPreferences = {
nodeIntegration: false, // 禁用 Node.js 集成
contextIsolation: true, // 启用上下文隔离
enableRemoteModule: false, // 禁用 remote 模块
preload: path.join(__dirname, 'preload.js'), // 使用预加载脚本
sandbox: true // 启用沙箱模式(可选)
}
Preload 脚本
Preload 脚本在渲染进程中运行,但在网页脚本执行之前加载,是连接主进程和渲染进程的桥梁。
创建 preload.js
:
javascript
const { contextBridge, ipcRenderer } = require('electron')
// 向渲染进程暴露安全的 API
contextBridge.exposeInMainWorld('electronAPI', {
// 获取应用信息
getAppInfo: () => ipcRenderer.invoke('get-app-info'),
// 显示消息框
showMessageBox: (options) => ipcRenderer.invoke('show-message-box', options),
// 监听主进程消息
onMessage: (callback) => {
ipcRenderer.on('main-message', (event, data) => callback(data))
},
// 发送消息到主进程
sendMessage: (message) => {
ipcRenderer.send('renderer-message', message)
}
})
// 在页面加载完成后执行
window.addEventListener('DOMContentLoaded', () => {
console.log('Preload script loaded')
})
渲染进程示例
创建 renderer.js
:
javascript
class RendererProcess {
constructor() {
this.init()
}
async init() {
await this.loadAppInfo()
this.setupEventListeners()
}
async loadAppInfo() {
try {
const appInfo = await window.electronAPI.getAppInfo()
this.displayAppInfo(appInfo)
} catch (error) {
console.error('获取应用信息失败:', error)
}
}
displayAppInfo(info) {
const container = document.getElementById('app-info')
if (container) {
container.innerHTML = `
<h3>应用信息</h3>
<p><strong>名称:</strong> ${info.name}</p>
<p><strong>版本:</strong> ${info.version}</p>
<p><strong>平台:</strong> ${info.platform}</p>
<p><strong>架构:</strong> ${info.arch}</p>
`
}
}
setupEventListeners() {
// 按钮点击事件
const messageBtn = document.getElementById('show-message')
if (messageBtn) {
messageBtn.addEventListener('click', this.showMessage.bind(this))
}
// 监听主进程消息
window.electronAPI.onMessage((data) => {
console.log('收到主进程消息:', data)
})
}
async showMessage() {
const options = {
type: 'info',
title: '来自渲染进程的消息',
message: '这是一个来自渲染进程的消息框',
buttons: ['确定', '取消']
}
try {
const result = await window.electronAPI.showMessageBox(options)
console.log('用户选择:', result.response)
} catch (error) {
console.error('显示消息框失败:', error)
}
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
new RendererProcess()
})
2.4 进程生命周期管理
主进程生命周期
javascript
const { app } = require('electron')
// 应用事件监听
app.on('ready', () => {
console.log('应用已准备就绪')
})
app.on('window-all-closed', () => {
console.log('所有窗口已关闭')
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('before-quit', (event) => {
console.log('应用即将退出')
// 可以调用 event.preventDefault() 阻止退出
})
app.on('will-quit', (event) => {
console.log('应用将要退出')
// 最后的清理机会
})
app.on('activate', () => {
console.log('应用被激活 (macOS)')
})
渲染进程生命周期
javascript
// 页面加载事件
window.addEventListener('DOMContentLoaded', () => {
console.log('DOM 内容已加载')
})
window.addEventListener('load', () => {
console.log('页面完全加载')
})
window.addEventListener('beforeunload', (event) => {
console.log('页面即将卸载')
// 可以显示确认对话框
event.returnValue = '确定要离开吗?'
})
window.addEventListener('unload', () => {
console.log('页面正在卸载')
})
2.5 进程间的数据共享
共享数据的方式
- IPC 通信 (推荐)
- 文件系统
- 数据库
- 内存映射文件
最佳实践
javascript
// 主进程中的数据管理
class DataManager {
constructor() {
this.appData = {
settings: {},
userPreferences: {},
cache: new Map()
}
}
getData(key) {
return this.appData[key]
}
setData(key, value) {
this.appData[key] = value
// 通知所有渲染进程数据更新
this.notifyRenderers('data-updated', { key, value })
}
notifyRenderers(channel, data) {
BrowserWindow.getAllWindows().forEach(window => {
window.webContents.send(channel, data)
})
}
}
2.6 调试技巧
主进程调试
bash
# 启用主进程调试
electron --inspect=5858 .
# 或者在代码中添加
process.debugPort = 5858
渲染进程调试
javascript
// 在主进程中打开开发者工具
mainWindow.webContents.openDevTools()
// 或者使用快捷键 F12
日志管理
javascript
// 主进程日志
const log = require('electron-log')
log.info('这是一条信息日志')
log.error('这是一条错误日志')
// 渲染进程日志
console.log('渲染进程日志')
2.7 性能优化建议
合理使用进程
- 避免创建过多窗口
- 及时关闭不需要的窗口
内存管理
- 监控内存使用情况
- 及时清理不需要的数据
IPC 优化
- 减少不必要的通信
- 使用批量操作
本章小结
通过本章学习,你应该已经:
- ✅ 理解了 Electron 的双进程架构
- ✅ 掌握了主进程和渲染进程的职责分工
- ✅ 学会了使用 Preload 脚本进行安全通信
- ✅ 了解了进程生命周期管理
- ✅ 掌握了基本的调试技巧
在下一章中,我们将深入学习 BrowserWindow API 和窗口管理技巧!