Electron 是内部集成了两个运行环境:
- Nodejs 环境,称为主进程(Main Process)
- Chromium 环境,称为渲染器进程(Renderer Process)

可以理解成在主进程中就是在写 Nodejs 的代码,在渲染器进程中是在写网页相关的代码(如 HTML、JavaScript等)。
主进程对应的代码文件是 main.js以及将来通过 reguire 导入到 main.js 中的模块
渲染器进程对应的代码文件是 index.html以及将来通过link、script、img引入到页面中的代码或资源
1.主进程
在 Electron 应用中,主进程(Main Process) 是整个应用的核心控制中枢,负责管理应用的生命周期、窗口创建与管理、系统资源访问等核心功能。它是 Electron 架构中不可或缺的一部分,与渲染进程(Renderer Process)共同构成了应用的运行体系。
主进程是 Electron 应用启动时首先执行的进程,由 Node.js 驱动,其入口文件通常在 package.json
中通过 main
字段指定(例如 main: "main.js"
)。一个 Electron 应用只有一个主进程,它是应用的 "大脑",负责统筹和控制整个应用的行为。
主要职责
-
管理应用生命周期 :主进程通过 Electron 提供的
app
模块控制应用的启动、就绪、窗口关闭、退出等完整生命周期:- 监听
ready
事件:应用初始化完成后触发,通常在此创建第一个窗口。 - 监听
window-all-closed
事件:所有窗口关闭时触发,通常在此处理应用退出(macOS 除外,因 macOS 应用关闭所有窗口后仍可能驻留 Dock)。 - 监听
activate
事件:macOS 中点击 Dock 图标时触发,通常在此重新创建窗口(如果所有窗口已关闭)。
- 监听
-
创建和管理窗口 :主进程通过
BrowserWindow
模块创建和管理应用窗口(每个窗口对应一个渲染进程)。它可以控制窗口的尺寸、位置、样式(如边框、透明度)、菜单等,并能监听窗口的生命周期(如打开、关闭、最小化)。
js
const { BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
title: "我的Electron应用"
});
win.loadFile('index.html'); // 加载窗口要显示的页面
}
-
访问系统级资源: 主进程运行在 Node.js 环境中,可直接调用 Node.js API 和 Electron 提供的系统级 API,实现渲染进程无法直接完成的功能:
- 文件系统操作(通过
fs
模块读写本地文件)。 - 系统对话框(如
dialog.showOpenDialog
打开文件选择框)。 - 系统托盘(
Tray
模块创建托盘图标和菜单)。 - 全局快捷键(
globalShortcut
注册系统级快捷键)。 - 网络请求(通过
net
模块或 Node.js 的http
模块)。
- 文件系统操作(通过
-
进程间通信(IPC)的 "服务器端" :主进程与渲染进程(每个窗口一个)无法直接共享内存,需通过 IPC(进程间通信) 交互。主进程通过
ipcMain
模块监听渲染进程发送的消息,并回复结果。
示例:主进程接收渲染进程消息
js
const { ipcMain } = require('electron');
// 监听渲染进程发送的 "read-file" 消息
ipcMain.handle('read-file', async (event, filePath) => {
const fs = require('fs').promises;
try {
const content = await fs.readFile(filePath, 'utf8');
return { success: true, content };
} catch (err) {
return { success: false, error: err.message };
}
})
- 管理应用菜单和托盘 :主进程负责创建应用的顶部菜单(
Menu
模块)和系统托盘菜单(Tray
模块),这些菜单的点击事件由主进程处理,可触发窗口操作、应用设置等功能。
核心特性
- 唯一性:一个 Electron 应用只有一个主进程,所有窗口和渲染进程都由它创建和管理。
- Node.js 环境 :主进程可直接使用 Node.js 的所有 API(如
fs
、path
、os
等),具备操作系统级别的访问能力。 - 不可渲染界面:主进程本身不负责页面渲染,界面渲染由其创建的渲染进程完成。
- 崩溃影响全局:主进程崩溃会导致整个应用退出,而单个渲染进程崩溃仅影响对应的窗口。
与渲染进程的关系
- 主进程是 "父进程",渲染进程是 "子进程":渲染进程由主进程通过
BrowserWindow
创建,依附于主进程存在。 - 通信依赖 IPC:主进程通过
ipcMain
接收消息,渲染进程通过ipcRenderer
发送 / 接收消息,两者通过异步消息传递数据。 - 功能分工明确:主进程管 "全局控制" 和 "系统交互",渲染进程管 "页面渲染" 和 "用户交互"。
2.渲染器进程
在 Electron 应用中,渲染进程(Renderer Process) 是负责展示用户界面、处理页面交互的进程,本质上是基于 Chromium 浏览器内核的 "网页渲染器"。每个 Electron 窗口(由主进程通过 BrowserWindow
创建)对应一个独立的渲染进程,负责解析和渲染 HTML、CSS,执行前端 JavaScript,以及响应用户的交互操作。
核心定义
渲染进程是 Electron 中 "负责界面展示与用户交互" 的进程,运行在 Chromium 的渲染引擎环境中,其核心功能与普通浏览器的标签页类似 ------ 将 Web 资源(HTML/CSS/JS)渲染为可视化界面,并处理页面内的事件(如点击、输入等)。
一个 Electron 应用可以有多个渲染进程(取决于打开的窗口数量),且每个渲染进程相互独立,一个窗口的渲染进程崩溃不会影响其他窗口或主进程。
主要职责
- 渲染页面内容 :解析并渲染 HTML 结构、CSS 样式,将 Web 资源转换为用户可见的界面。例如,加载
index.html
后,渲染进程会解析 DOM 树、CSS OM 树,最终生成渲染树并绘制到窗口中。 - 处理前端交互:执行页面中的 JavaScript 代码,响应用户操作(如点击按钮、输入文本、滚动页面等),处理 DOM 操作、事件绑定、前端框架逻辑(如 React/Vue 组件的状态更新)等。
- 与主进程通信 :渲染进程无法直接访问系统级资源(如文件系统、系统托盘等),需通过 IPC(进程间通信) 机制与主进程交互,委托主进程完成 "前端权限之外" 的操作(如读取本地文件、弹出系统对话框等)。
- 管理页面生命周期 :处理页面的加载、刷新、导航、关闭等生命周期事件,例如通过
window.onload
监听页面加载完成,通过beforeunload
监听页面关闭前的确认等。
运行环境与限制
渲染进程的运行环境类似于浏览器,但有一些关键差异:
- 基础环境 :基于 Chromium 的 V8 引擎,支持大多数浏览器 API(如
window
、document
、fetch
等)和 ES 标准语法。 - Node.js 访问控制 :默认情况下,渲染进程不具备 Node.js 环境访问权限 (出于安全考虑),无法直接使用
fs
、path
等 Node 模块。若需访问,需在创建窗口时通过webPreferences
配置:
js
// 主进程中创建窗口时的配置
new BrowserWindow({
webPreferences: {
nodeIntegration: false, // 不建议开启,存在安全风险
contextIsolation: true, // 启用上下文隔离(默认开启)
preload: path.join(__dirname, 'preload.js') // 预加载脚本(安全的交互方式)
}
});
- 上下文隔离 :默认启用
contextIsolation: true
,意味着渲染进程的前端 JS(如页面中的script
)与预加载脚本(preload.js
)运行在不同的上下文,避免全局变量污染和安全风险。
与主进程的通信(IPC)
渲染进程通过 ipcRenderer
模块与主进程通信,支持 "发送消息" 和 "接收消息" 双向交互:
渲染进程发送消息,主进程响应例如,渲染进程请求主进程读取本地文件:
js
// 渲染进程(页面中的 JS 或 preload.js)
const { ipcRenderer } = require('electron');
// 发送消息并等待回复(异步)
async function readLocalFile(filePath) {
try {
const result = await ipcRenderer.invoke('read-file', filePath);
if (result.success) {
console.log('文件内容:', result.content);
} else {
console.error('错误:', result.error);
}
} catch (err) {
console.error('通信失败:', err);
}
}
主进程发送消息,渲染进程接收例如,主进程向渲染进程推送通知:
js
// 渲染进程中监听主进程消息
ipcRenderer.on('app-notification', (event, message) => {
alert(`收到通知: ${message}`);
});
核心特性
- 独立性:每个窗口对应一个渲染进程,进程间相互隔离,一个窗口崩溃不影响其他窗口。
- 前端技术兼容:支持所有 Web 标准技术(HTML5/CSS3/ES6+)和主流前端框架(React、Vue、Svelte 等),开发者可复用 Web 开发经验。
- 安全限制:默认受限于浏览器安全模型(如同源策略),且无法直接访问系统资源,需通过主进程代理,降低恶意代码风险。
- 调试便捷 :可通过
win.webContents.openDevTools()
打开 Chromium 开发者工具,像调试网页一样调试渲染进程(查看 DOM、Console、Network 等)。
与主进程的对比
维度 | 主进程(Main Process) | 渲染进程(Renderer Process) |
---|---|---|
数量 | 整个应用唯一 | 每个窗口一个,可多个 |
核心职责 | 管理应用生命周期、窗口、系统资源访问 | 渲染界面、处理前端交互、用户输入响应 |
运行环境 | Node.js 环境,可直接访问系统 API | 类浏览器环境,默认无 Node.js 权限 |
崩溃影响 | 导致整个应用退出 | 仅影响对应窗口,不影响其他进程 |
IPC 模块 | 使用 ipcMain 接收 / 回复消息 |
使用 ipcRenderer 发送 / 接收消息 |
3.预加载脚本
在 Electron 中,预加载脚本(preload.js) 是一个特殊的脚本文件,它运行在渲染进程的上下文环境中,但执行时机介于 "主进程创建窗口" 和 "渲染进程加载网页内容" 之间。其核心作用是在保持安全性的前提下,为渲染进程(前端页面)和主进程之间搭建一个可控的通信桥梁,同时提供有限的 Node.js 能力访问。
为什么需要预加载脚本?
Electron 为了安全性,默认启用了 上下文隔离(contextIsolation: true) 和 禁用 Node.js 集成(nodeIntegration: false) :
- 上下文隔离:渲染进程的前端 JavaScript(如页面中的
<script>
)与预加载脚本运行在完全隔离的上下文环境中,避免全局变量污染和恶意代码篡改。 - 禁用 Node.js 集成:渲染进程默认无法直接访问 Node.js API(如
fs
、path
)和 Electron 主进程 API,防止前端脚本滥用系统权限。
这种设计虽然提升了安全性,但也导致渲染进程(前端)无法直接与主进程通信或使用系统功能。而预加载脚本正是为了解决这个矛盾而存在 ------ 它运行在一个 "特权但受限" 的环境中,既能访问部分 Node.js API 和 Electron 的 ipcRenderer
(进程间通信模块),又能安全地向渲染进程暴露有限的接口。
核心作用
- 安全暴露 IPC 通信接口 :预加载脚本的主要功能是通过
contextBridge
模块,将主进程与渲染进程的通信方法(如ipcRenderer.invoke
、ipcRenderer.on
)安全地暴露给前端页面,让前端可以间接调用主进程的功能(如读取文件、操作系统托盘等),同时避免直接暴露ipcRenderer
带来的安全风险。
js
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
// 通过 contextBridge 向渲染进程的 window 对象暴露安全接口
contextBridge.exposeInMainWorld('electronAPI', {
// 暴露一个获取文件内容的方法,前端可调用
readFile: (filePath) => ipcRenderer.invoke('read-file', filePath),
// 暴露一个接收主进程消息的监听方法
onNotification: (callback) => ipcRenderer.on('app-notify', (event, args) => callback(args))
});
前端页面(渲染进程)即可安全调用这些接口:
js
// 页面中的 JavaScript
// 调用预加载脚本暴露的 readFile 方法
window.electronAPI.readFile('/path/to/file.txt')
.then(result => console.log('文件内容:', result))
.catch(err => console.error('错误:', err));
// 监听主进程发送的通知
window.electronAPI.onNotification(message => {
alert(`收到通知: ${message}`);
});
- 提供有限的 Node.js 能力 :预加载脚本运行在具有部分 Node.js 权限的环境中(即使
nodeIntegration: false
),可以访问一些 Node.js 核心模块(如path
、os
)或 Electron 特定 API。但需注意:
- 不能直接将 Node.js 模块暴露给前端(如
contextBridge.exposeInMainWorld('fs', fs)
存在安全风险)。 - 应仅暴露 "前端必需且经过验证" 的功能,例如获取系统信息的简化接口:
js
// preload.js
const { contextBridge } = require('electron');
const os = require('os');
contextBridge.exposeInMainWorld('systemInfo', {
getPlatform: () => os.platform(), // 仅暴露获取系统平台的方法
getHostname: () => os.hostname() // 仅暴露获取主机名的方法
});
-
页面加载前的初始化操作:预加载脚本在渲染进程的 DOM 初始化完成后、页面脚本加载前执行,因此可以用于:
- 设置全局变量或事件监听(如
window.addEventListener('beforeunload', ...)
)。 - 初始化前端依赖的基础数据(如用户配置、环境变量等)。
- 拦截或处理页面请求(需配合
webRequest
等 API)。
- 设置全局变量或事件监听(如
关键特性
- 运行时机 :在渲染进程的
window
对象创建后、页面 HTML/CSS/JS 加载前执行,确保前端脚本运行时,预加载的接口已可用。 - 权限边界 :可访问
ipcRenderer
、部分 Node.js 模块和 Electron API,但受限于上下文隔离,无法直接修改渲染进程的全局变量(除非通过contextBridge
)。 - 安全性 :通过
contextBridge
暴露的接口是只读的,前端无法篡改或滥用,避免了直接暴露ipcRenderer
可能导致的安全漏洞(如恶意脚本发送伪造 IPC 消息)。