Electron 核心概念与架构

深入理解 Electron 的多进程架构、主进程与渲染进程的角色分工,以及为什么 Electron 能让前端工程师开发桌面应用。


为什么 Electron 能让前端工程师开发桌面应用

Electron 的核心理念是:用你已经熟悉的 Web 技术栈(HTML、CSS、JavaScript)开发跨平台桌面应用

Electron = Chromium(浏览器渲染引擎)+ Node.js(运行时)+ 原生操作系统 API。

这意味着:

  • 你已经会写 Web 应用,就意味着你会写 Electron 应用
  • 一套代码,同时输出 Windows、macOS、Linux 三个平台的安装包
  • 可以使用 npm 生态中的所有包

Electron 的多进程架构

Electron 和 Chrome 一样,采用多进程架构:

主进程(Main Process)

  • 一个 Electron 应用只有一个主进程
  • 运行在 Node.js 环境,可以调用所有 Node.js API
  • 负责创建 Renderer 进程、管理应用窗口、调用系统原生 API
  • 可以被认为是应用的"后台"

渲染进程(Renderer Process)

  • 每个 BrowserWindow(窗口)对应一个渲染进程
  • 运行在 Chromium 环境,隔离于 Node.js
  • 负责页面渲染、JavaScript 执行
  • 只能通过 IPC 和主进程通信

GPU 进程

  • 负责 GPU 相关的图形渲染

主进程与渲染进程的关系

┌─────────────────┐
│   Main Process  │  Node.js 环境
│  (Node APIs)   │  - 文件系统访问
└────────┬────────┘  - 原生对话框
         │ IPC
┌────────┴────────┐
│ Renderer Process │  Chromium 环境
│  (Web APIs)    │  - DOM、CSS
└─────────────────┘  - JavaScript(无 Node)

关键约束

渲染进程默认没有 Node.js 访问权限。这意味着:

  • 无法直接 require('fs') 读取文件
  • 无法调用 Node.js 原生模块
  • 无法访问操作系统 API

这些能力都在主进程,渲染进程需要通过 IPC 请求主进程完成。


BrowserWindow

窗口是 Electron 应用的核心 UI 单位:

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

const mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  title: 'My App',
  webPreferences: {
    nodeIntegration: false,    // 默认关闭 Node
    contextIsolation: true,   // 隔离上下文
    preload: path.join(__dirname, 'preload.js')
  }
});

mainWindow.loadFile(path.join(__dirname, 'index.html'));

BrowserWindow 的生命周期

mainWindow.on('closed', () => {
  mainWindow = null; // 窗口关闭后清理引用
});

Context Isolation 与 Preload

Context Isolation

contextIsolation: true 时,每个渲染进程有独立的 JavaScript 上下文:

webPreferences: {
  contextIsolation: true, // 隔离上下文
  nodeIntegration: false
}

这防止了不同窗口之间的变量污染,也防止了恶意脚本攻击。

Preload Script

Preload 是在渲染进程加载之前执行的脚本,可以安全地暴露部分主进程 API:

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

// 暴露安全的 API 给渲染进程
contextBridge.exposeInMainWorld('electronAPI', {
  readFile: (filePath) => ipcRenderer.invoke('file:read', filePath),
  saveFile: (filePath, content) =>
    ipcRenderer.invoke('file:save', filePath, content)
});
// renderer.js(渲染进程)
window.electronAPI.readFile('/path/to/file').then(content => {
  console.log(content);
});

进程间通信(IPC)

IPC 基础

渲染进程和主进程通过 IPC 通信:

// 主进程监听
ipcMain.handle('file:read', async (event, filePath) => {
  const fs = require('fs').promises;
  return await fs.readFile(filePath, 'utf-8');
});

// 渲染进程调用
const content = await window.electronAPI.readFile('/path/to/file');

双向通信

// 渲染进程向主进程发送消息
ipcRenderer.send('message:send', { text: 'Hello' });

// 主进程接收并回复
ipcMain.on('message:send', (event, data) => {
  console.log('Received:', data.text);
  event.reply('message:reply', { text: 'World' });
});

Electron 的实际项目结构

my-app/
├── package.json
├── main.js           # 主进程入口
├── preload.js         # Preload 脚本
├── src/
│   ├── index.html    # 页面
│   ├── renderer.js   # 渲染进程 JS
│   └── styles.css   # 样式
└── assets/          # 静态资源

这一章想说的

Electron 的架构核心是多进程

  1. 主进程:Node.js 环境,负责系统交互
  2. 渲染进程:Chromium 环境,负责页面渲染
  3. IPC:进程间通信,渲染进程通过 IPC 调用主进程能力

安全最佳实践:

  • contextIsolation: true
  • nodeIntegration: false
  • 使用 Preload 暴露安全的 API

延展阅读