主进程与渲染进程 #

一、进程模型概述 #

1.1 Electron 进程架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                      Electron 应用                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────┐                                        │
│  │     主进程       │  ← 唯一,应用入口                       │
│  │   (Main)        │                                        │
│  │                 │                                        │
│  │  - Node.js 环境  │                                        │
│  │  - 管理窗口      │                                        │
│  │  - 系统集成      │                                        │
│  │  - 原生 API     │                                        │
│  └────────┬────────┘                                        │
│           │                                                 │
│           │ 创建                                            │
│           ▼                                                 │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │   渲染进程 1     │  │   渲染进程 2     │  ← 多个,每个窗口一个 │
│  │  (Renderer)     │  │  (Renderer)     │                   │
│  │                 │  │                 │                   │
│  │  - Chromium 环境 │  │  - Chromium 环境 │                   │
│  │  - Web 页面渲染  │  │  - Web 页面渲染  │                   │
│  │  - 用户交互      │  │  - 用户交互      │                   │
│  └─────────────────┘  └─────────────────┘                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 进程对比 #

特性 主进程 渲染进程
数量 唯一 可多个
运行环境 Node.js Chromium
入口文件 package.json 中的 main BrowserWindow 加载的页面
主要职责 管理应用生命周期、窗口 渲染页面、处理交互
可访问 API 所有 Electron API 部分 Electron API
DOM 访问 不可访问 可访问

二、主进程详解 #

2.1 主进程职责 #

javascript
// main.js - 主进程入口
const { app, BrowserWindow, ipcMain, Menu, Tray } = require('electron');

// 1. 管理应用生命周期
app.whenReady().then(() => {
    createWindow();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// 2. 创建和管理窗口
function createWindow() {
    const win = new BrowserWindow({
        width: 800,
        height: 600
    });
    win.loadFile('index.html');
}

// 3. 处理 IPC 通信
ipcMain.handle('get-app-path', () => {
    return app.getAppPath();
});

// 4. 创建原生 UI 元素
function createMenu() {
    const menu = Menu.buildFromTemplate([
        { label: '文件', submenu: [{ role: 'quit' }] }
    ]);
    Menu.setApplicationMenu(menu);
}

// 5. 系统集成
function createTray() {
    const tray = new Tray('icon.png');
    tray.setToolTip('我的应用');
}

2.2 主进程可访问的模块 #

模块 说明
app 应用生命周期控制
BrowserWindow 窗口管理
ipcMain IPC 通信(主进程端)
Menu 应用菜单
Tray 系统托盘
dialog 原生对话框
Notification 系统通知
shell 系统集成
session 会话管理
net 网络请求
clipboard 剪贴板
globalShortcut 全局快捷键
powerMonitor 电源监控
screen 屏幕信息
nativeTheme 系统主题

2.3 主进程最佳实践 #

javascript
// 1. 单例模式确保只有一个实例
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
    app.quit();
} else {
    app.on('second-instance', () => {
        if (mainWindow) {
            mainWindow.show();
            mainWindow.focus();
        }
    });
}

// 2. 窗口引用管理
const windows = new Set();

function createWindow() {
    const win = new BrowserWindow({});
    windows.add(win);
    win.on('closed', () => {
        windows.delete(win);
    });
}

// 3. 错误处理
process.on('uncaughtException', (error) => {
    console.error('未捕获的异常:', error);
});

// 4. 优雅退出
let isQuitting = false;

app.on('before-quit', () => {
    isQuitting = true;
});

function closeWindow(win) {
    if (isQuitting) {
        win.destroy();
    } else {
        win.hide();
    }
}

三、渲染进程详解 #

3.1 渲染进程职责 #

javascript
// renderer.js - 渲染进程脚本

// 1. DOM 操作
document.getElementById('btn').addEventListener('click', () => {
    console.log('按钮被点击');
});

// 2. UI 渲染
function renderList(items) {
    const list = document.getElementById('list');
    list.innerHTML = items.map(item => `<li>${item}</li>`).join('');
}

// 3. 用户交互
document.querySelector('form').addEventListener('submit', (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    // 处理表单数据
});

// 4. 通过 IPC 与主进程通信
const { ipcRenderer } = require('electron');

async function getAppPath() {
    const path = await ipcRenderer.invoke('get-app-path');
    return path;
}

3.2 渲染进程可访问的模块 #

模块 说明
ipcRenderer IPC 通信(渲染进程端)
desktopCapturer 桌面捕获
contextBridge 上下文桥接(预加载脚本)
webFrame Web 框架控制

3.3 渲染进程安全配置 #

javascript
// main.js - 创建窗口时的安全配置
const mainWindow = new BrowserWindow({
    webPreferences: {
        // 安全配置
        nodeIntegration: false,           // 禁用 Node.js 集成
        contextIsolation: true,           // 启用上下文隔离
        enableRemoteModule: false,        // 禁用 remote 模块
        sandbox: true,                    // 启用沙箱
        webSecurity: true,                // 启用 Web 安全
        
        // 预加载脚本
        preload: path.join(__dirname, 'preload.js')
    }
});

四、进程间通信 #

4.1 IPC 通信模型 #

text
┌─────────────────┐                    ┌─────────────────┐
│    渲染进程      │                    │     主进程       │
│                 │                    │                 │
│  ipcRenderer    │                    │    ipcMain      │
│                 │                    │                 │
│  send()  ──────►│  IPC Channel      │◄─────── on()    │
│                 │  ────────────────►│                 │
│  ◄───── on()    │                    │  invoke() ◄────│
│                 │                    │                 │
│  invoke() ─────►│                    │◄─── handle()   │
│                 │                    │                 │
└─────────────────┘                    └─────────────────┘

4.2 基本通信方式 #

方式一:send/on 模式

javascript
// 渲染进程发送
ipcRenderer.send('channel-name', data);

// 主进程接收
ipcMain.on('channel-name', (event, data) => {
    console.log('收到数据:', data);
    
    // 回复消息
    event.reply('channel-name-reply', responseData);
});

// 渲染进程接收回复
ipcRenderer.on('channel-name-reply', (event, data) => {
    console.log('收到回复:', data);
});

方式二:invoke/handle 模式(推荐)

javascript
// 主进程:注册处理器
ipcMain.handle('get-user-info', async (event, userId) => {
    const user = await fetchUserInfo(userId);
    return user;
});

// 渲染进程:调用并获取结果
const user = await ipcRenderer.invoke('get-user-info', 123);
console.log('用户信息:', user);

4.3 预加载脚本桥接 #

javascript
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

// 安全地暴露 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
    // 获取用户信息
    getUserInfo: (userId) => ipcRenderer.invoke('get-user-info', userId),
    
    // 发送消息
    sendMessage: (message) => ipcRenderer.send('send-message', message),
    
    // 监听消息
    onMessage: (callback) => {
        ipcRenderer.on('receive-message', (event, data) => callback(data));
    },
    
    // 移除监听
    removeListener: (callback) => {
        ipcRenderer.removeListener('receive-message', callback);
    }
});
javascript
// renderer.js - 使用暴露的 API
const userInfo = await window.electronAPI.getUserInfo(123);

window.electronAPI.onMessage((data) => {
    console.log('收到消息:', data);
});

五、多窗口管理 #

5.1 多窗口架构 #

text
┌─────────────────────────────────────────┐
│              主进程                      │
│                                         │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │ 窗口 1   │ │ 窗口 2   │ │ 窗口 3   │   │
│  │ 主窗口   │ │ 设置窗口 │ │ 关于窗口 │   │
│  └────┬────┘ └────┬────┘ └────┬────┘   │
│       │           │           │         │
└───────┼───────────┼───────────┼─────────┘
        │           │           │
        ▼           ▼           ▼
   ┌─────────┐ ┌─────────┐ ┌─────────┐
   │渲染进程1│ │渲染进程2│ │渲染进程3│
   └─────────┘ └─────────┘ └─────────┘

5.2 窗口管理器实现 #

javascript
// electron/main/windowManager.js
const { BrowserWindow } = require('electron');
const path = require('path');

class WindowManager {
    constructor() {
        this.windows = new Map();
    }

    createWindow(name, options) {
        const win = new BrowserWindow({
            ...options,
            webPreferences: {
                preload: path.join(__dirname, '../preload/index.js'),
                nodeIntegration: false,
                contextIsolation: true
            }
        });

        this.windows.set(name, win);

        win.on('closed', () => {
            this.windows.delete(name);
        });

        return win;
    }

    getWindow(name) {
        return this.windows.get(name);
    }

    closeWindow(name) {
        const win = this.windows.get(name);
        if (win) {
            win.close();
        }
    }

    closeAllWindows() {
        for (const win of this.windows.values()) {
            win.close();
        }
    }

    broadcast(channel, data) {
        for (const win of this.windows.values()) {
            win.webContents.send(channel, data);
        }
    }
}

module.exports = new WindowManager();

5.3 窗口间通信 #

javascript
// 主进程:转发消息到其他窗口
ipcMain.on('broadcast-message', (event, data) => {
    const senderWin = BrowserWindow.fromWebContents(event.sender);
    
    for (const [name, win] of windowManager.windows) {
        if (win !== senderWin) {
            win.webContents.send('receive-broadcast', data);
        }
    }
});

// 渲染进程:发送广播
ipcRenderer.send('broadcast-message', {
    type: 'update',
    data: { key: 'value' }
});

// 渲染进程:接收广播
ipcRenderer.on('receive-broadcast', (event, data) => {
    console.log('收到广播:', data);
});

六、进程生命周期 #

6.1 主进程生命周期 #

text
┌─────────────┐
│  app.ready  │  应用准备就绪
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 创建窗口     │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ app.activate│  macOS 激活(点击 Dock 图标)
└──────┬──────┘
       │
       ▼
┌─────────────────────┐
│ window-all-closed   │  所有窗口关闭
└──────┬──────────────┘
       │
       ▼
┌─────────────┐
│ app.quit    │  应用退出
└─────────────┘

6.2 渲染进程生命周期 #

javascript
// 页面加载完成
window.addEventListener('DOMContentLoaded', () => {
    console.log('DOM 加载完成');
});

// 页面完全加载
window.addEventListener('load', () => {
    console.log('页面加载完成');
});

// 页面即将卸载
window.addEventListener('beforeunload', (e) => {
    e.returnValue = '确定要离开吗?';
});

// 页面卸载
window.addEventListener('unload', () => {
    console.log('页面卸载');
});

七、进程调试 #

7.1 主进程调试 #

方式一:控制台输出

javascript
console.log('主进程日志');
console.error('错误信息');

方式二:VS Code 调试

json
// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Main Process",
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
            "args": ["."],
            "outputCapture": "std"
        }
    ]
}

方式三:Chrome DevTools

bash
# 启动时开启调试端口
electron . --inspect=5858

# 然后在 Chrome 中访问
# chrome://inspect

7.2 渲染进程调试 #

javascript
// 在主进程中打开开发者工具
mainWindow.webContents.openDevTools();

// 或使用快捷键
// Windows/Linux: Ctrl + Shift + I
// macOS: Cmd + Option + I

八、性能优化 #

8.1 进程优化 #

javascript
// 1. 延迟加载窗口
function createWindow() {
    const win = new BrowserWindow({
        show: false  // 先不显示
    });
    
    win.loadFile('index.html');
    
    // 页面加载完成后再显示
    win.once('ready-to-show', () => {
        win.show();
    });
}

// 2. 窗口复用
let settingsWindow = null;

function openSettings() {
    if (settingsWindow) {
        settingsWindow.focus();
        return;
    }
    
    settingsWindow = new BrowserWindow({});
    settingsWindow.on('closed', () => {
        settingsWindow = null;
    });
}

// 3. 减少渲染进程数量
// 使用 <webview> 标签代替多窗口

8.2 内存管理 #

javascript
// 及时清理不需要的窗口引用
let mainWindow = null;

function createWindow() {
    mainWindow = new BrowserWindow({});
    
    mainWindow.on('closed', () => {
        mainWindow = null;  // 清理引用
    });
}

// 清理事件监听器
ipcRenderer.on('some-event', handler);

// 不需要时移除
ipcRenderer.removeListener('some-event', handler);

九、总结 #

9.1 核心要点 #

要点 说明
主进程 唯一,管理应用生命周期和窗口
渲染进程 多个,负责页面渲染和用户交互
IPC 通信 主进程与渲染进程之间的桥梁
预加载脚本 安全地暴露 API 给渲染进程

9.2 下一步 #

现在你已经理解了 Electron 的进程模型,接下来让我们学习 进程间通信IPC,深入掌握进程通信的各种方式!

最后更新:2026-03-28