为什么 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 的架构核心是多进程:
- 主进程:Node.js 环境,负责系统交互
- 渲染进程:Chromium 环境,负责页面渲染
- IPC:进程间通信,渲染进程通过 IPC 调用主进程能力
安全最佳实践:
contextIsolation: truenodeIntegration: false- 使用 Preload 暴露安全的 API