Electron基本概念

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 应用只有一个主进程,它是应用的 "大脑",负责统筹和控制整个应用的行为。

主要职责

  1. 管理应用生命周期 :主进程通过 Electron 提供的 app 模块控制应用的启动、就绪、窗口关闭、退出等完整生命周期:

    • 监听 ready 事件:应用初始化完成后触发,通常在此创建第一个窗口。
    • 监听 window-all-closed 事件:所有窗口关闭时触发,通常在此处理应用退出(macOS 除外,因 macOS 应用关闭所有窗口后仍可能驻留 Dock)。
    • 监听 activate 事件:macOS 中点击 Dock 图标时触发,通常在此重新创建窗口(如果所有窗口已关闭)。
  2. 创建和管理窗口 :主进程通过 BrowserWindow 模块创建和管理应用窗口(每个窗口对应一个渲染进程)。它可以控制窗口的尺寸、位置、样式(如边框、透明度)、菜单等,并能监听窗口的生命周期(如打开、关闭、最小化)。

js 复制代码
const { BrowserWindow } = require('electron');
function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    title: "我的Electron应用"
  });
  win.loadFile('index.html'); // 加载窗口要显示的页面
}
  1. 访问系统级资源: 主进程运行在 Node.js 环境中,可直接调用 Node.js API 和 Electron 提供的系统级 API,实现渲染进程无法直接完成的功能:

    • 文件系统操作(通过 fs 模块读写本地文件)。
    • 系统对话框(如 dialog.showOpenDialog 打开文件选择框)。
    • 系统托盘(Tray 模块创建托盘图标和菜单)。
    • 全局快捷键(globalShortcut 注册系统级快捷键)。
    • 网络请求(通过 net 模块或 Node.js 的 http 模块)。
  2. 进程间通信(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 };
  }
})
  1. 管理应用菜单和托盘 :主进程负责创建应用的顶部菜单(Menu 模块)和系统托盘菜单(Tray 模块),这些菜单的点击事件由主进程处理,可触发窗口操作、应用设置等功能。

核心特性

  • 唯一性:一个 Electron 应用只有一个主进程,所有窗口和渲染进程都由它创建和管理。
  • Node.js 环境 :主进程可直接使用 Node.js 的所有 API(如 fspathos 等),具备操作系统级别的访问能力。
  • 不可渲染界面:主进程本身不负责页面渲染,界面渲染由其创建的渲染进程完成。
  • 崩溃影响全局:主进程崩溃会导致整个应用退出,而单个渲染进程崩溃仅影响对应的窗口。

与渲染进程的关系

  • 主进程是 "父进程",渲染进程是 "子进程":渲染进程由主进程通过 BrowserWindow 创建,依附于主进程存在。
  • 通信依赖 IPC:主进程通过 ipcMain 接收消息,渲染进程通过 ipcRenderer 发送 / 接收消息,两者通过异步消息传递数据。
  • 功能分工明确:主进程管 "全局控制" 和 "系统交互",渲染进程管 "页面渲染" 和 "用户交互"。

2.渲染器进程

在 Electron 应用中,渲染进程(Renderer Process) 是负责展示用户界面、处理页面交互的进程,本质上是基于 Chromium 浏览器内核的 "网页渲染器"。每个 Electron 窗口(由主进程通过 BrowserWindow 创建)对应一个独立的渲染进程,负责解析和渲染 HTML、CSS,执行前端 JavaScript,以及响应用户的交互操作。

核心定义

渲染进程是 Electron 中 "负责界面展示与用户交互" 的进程,运行在 Chromium 的渲染引擎环境中,其核心功能与普通浏览器的标签页类似 ------ 将 Web 资源(HTML/CSS/JS)渲染为可视化界面,并处理页面内的事件(如点击、输入等)。

一个 Electron 应用可以有多个渲染进程(取决于打开的窗口数量),且每个渲染进程相互独立,一个窗口的渲染进程崩溃不会影响其他窗口或主进程。

主要职责

  1. 渲染页面内容 :解析并渲染 HTML 结构、CSS 样式,将 Web 资源转换为用户可见的界面。例如,加载 index.html 后,渲染进程会解析 DOM 树、CSS OM 树,最终生成渲染树并绘制到窗口中。
  2. 处理前端交互:执行页面中的 JavaScript 代码,响应用户操作(如点击按钮、输入文本、滚动页面等),处理 DOM 操作、事件绑定、前端框架逻辑(如 React/Vue 组件的状态更新)等。
  3. 与主进程通信 :渲染进程无法直接访问系统级资源(如文件系统、系统托盘等),需通过 IPC(进程间通信) 机制与主进程交互,委托主进程完成 "前端权限之外" 的操作(如读取本地文件、弹出系统对话框等)。
  4. 管理页面生命周期 :处理页面的加载、刷新、导航、关闭等生命周期事件,例如通过 window.onload 监听页面加载完成,通过 beforeunload 监听页面关闭前的确认等。

运行环境与限制

渲染进程的运行环境类似于浏览器,但有一些关键差异:

  • 基础环境 :基于 Chromium 的 V8 引擎,支持大多数浏览器 API(如 windowdocumentfetch 等)和 ES 标准语法。
  • Node.js 访问控制 :默认情况下,渲染进程不具备 Node.js 环境访问权限 (出于安全考虑),无法直接使用 fspath 等 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(如 fspath)和 Electron 主进程 API,防止前端脚本滥用系统权限。

这种设计虽然提升了安全性,但也导致渲染进程(前端)无法直接与主进程通信或使用系统功能。而预加载脚本正是为了解决这个矛盾而存在 ------ 它运行在一个 "特权但受限" 的环境中,既能访问部分 Node.js API 和 Electron 的 ipcRenderer(进程间通信模块),又能安全地向渲染进程暴露有限的接口。

核心作用

  1. 安全暴露 IPC 通信接口 :预加载脚本的主要功能是通过 contextBridge 模块,将主进程与渲染进程的通信方法(如 ipcRenderer.invokeipcRenderer.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}`);
});
  1. 提供有限的 Node.js 能力 :预加载脚本运行在具有部分 Node.js 权限的环境中(即使 nodeIntegration: false),可以访问一些 Node.js 核心模块(如 pathos)或 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()  // 仅暴露获取主机名的方法
});
  1. 页面加载前的初始化操作:预加载脚本在渲染进程的 DOM 初始化完成后、页面脚本加载前执行,因此可以用于:

    • 设置全局变量或事件监听(如 window.addEventListener('beforeunload', ...))。
    • 初始化前端依赖的基础数据(如用户配置、环境变量等)。
    • 拦截或处理页面请求(需配合 webRequest 等 API)。

关键特性

  • 运行时机 :在渲染进程的 window 对象创建后、页面 HTML/CSS/JS 加载前执行,确保前端脚本运行时,预加载的接口已可用。
  • 权限边界 :可访问 ipcRenderer、部分 Node.js 模块和 Electron API,但受限于上下文隔离,无法直接修改渲染进程的全局变量(除非通过 contextBridge)。
  • 安全性 :通过 contextBridge 暴露的接口是只读的,前端无法篡改或滥用,避免了直接暴露 ipcRenderer 可能导致的安全漏洞(如恶意脚本发送伪造 IPC 消息)。
相关推荐
zhaoolee3 小时前
Claude Code使用指北(如何白嫖百万Qwen3 Token,每月劲省20刀)
前端
前台端水工程师3 小时前
vite-plugin-mock插件的3.0.2版本在生产环境无法使用
前端
戈卬3 小时前
VSCode 中 Prettier 工作原理与使用指南
前端
我叫张得帅3 小时前
从零开始的前端异世界生活--005--“HTTP详细解析中”
前端
Whbbit19993 小时前
在 Nestjs 中使用 Drizzle ORM
前端·javascript·nestjs
Never_Satisfied3 小时前
在JavaScript中,map方法使用指南
前端·javascript·vue.js
_码力全开_3 小时前
JavaScript从入门到实战 (1):JS 入门第一步:它是什么?能做什么?环境怎么搭?
开发语言·前端·javascript·新人首发
itslife3 小时前
vite 源码 - 执行 buildStart 钩子
前端·javascript
wsWmsw3 小时前
[译] 浏览器里的 Liquid Glass:利用 CSS 和 SVG 实现折射
前端·css·svg