Skip to main content

进程模型

进程模型

¥Process Model

Electron 继承了 Chromium 的多进程架构,这使得该框架在架构上与现代 Web 浏览器非常相似。本指南将扩展 教程 中应用的概念。

¥Electron inherits its multi-process architecture from Chromium, which makes the framework architecturally very similar to a modern web browser. This guide will expand on the concepts applied in the Tutorial.

为什么不是单一进程呢?

¥Why not a single process?

Web 浏览器是极其复杂的应用。除了显示 Web 内容的主要能力之外,他们还有许多次要职责,例如管理多个窗口(或选项卡)和加载第三方扩展。

¥Web browsers are incredibly complicated applications. Aside from their primary ability to display web content, they have many secondary responsibilities, such as managing multiple windows (or tabs) and loading third-party extensions.

在早期,浏览器通常使用单个进程来实现所有这些功能。尽管这种模式意味着你打开的每个选项卡的开销更少,但这也意味着一个网站崩溃或挂起会影响整个浏览器。

¥In the earlier days, browsers usually used a single process for all of this functionality. Although this pattern meant less overhead for each tab you had open, it also meant that one website crashing or hanging would affect the entire browser.

多进程模型

¥The multi-process model

为了解决这个问题,Chrome 团队决定每个选项卡都在自己的进程中渲染,从而限制网页上的错误或恶意代码可能对整个应用造成的危害。然后,单个浏览器进程控制这些进程以及整个应用生命周期。下面的 Chrome 漫画 图表直观地展示了该模型:

¥To solve this problem, the Chrome team decided that each tab would render in its own process, limiting the harm that buggy or malicious code on a web page could cause to the app as a whole. A single browser process then controls these processes, as well as the application lifecycle as a whole. This diagram below from the Chrome Comic visualizes this model:

Chrome's multi-process architecture

Electron 应用的结构非常相似。作为应用开发者,你可以控制两种类型的进程:mainrenderer。这些类似于上面概述的 Chrome 自己的浏览器和渲染器进程。

¥Electron applications are structured very similarly. As an app developer, you control two types of processes: main and renderer. These are analogous to Chrome's own browser and renderer processes outlined above.

主进程

¥The main process

每个 Electron 应用都有一个主进程,充当应用的入口点。主进程在 Node.js 环境中运行,这意味着它能够使用 require 模块并使用所有 Node.js API。

¥Each Electron app has a single main process, which acts as the application's entry point. The main process runs in a Node.js environment, meaning it has the ability to require modules and use all of Node.js APIs.

窗口管理

¥Window management

主进程的主要目的是使用 BrowserWindow 模块创建和管理应用窗口。

¥The main process' primary purpose is to create and manage application windows with the BrowserWindow module.

BrowserWindow 类的每个实例都会创建一个应用窗口,该窗口在单独的渲染器进程中加载网页。你可以使用窗口的 webContents 对象从主进程与此 Web 内容进行交互。

¥Each instance of the BrowserWindow class creates an application window that loads a web page in a separate renderer process. You can interact with this web content from the main process using the window's webContents object.

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

const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('https://github.com')

const contents = win.webContents
console.log(contents)

注意:还为 网络嵌入 创建了渲染器进程,例如 BrowserView 模块。嵌入 Web 内容也可访问 webContents 对象。

¥Note: A renderer process is also created for web embeds such as the BrowserView module. The webContents object is also accessible for embedded web content.

由于 BrowserWindow 模块是 EventEmitter,因此你还可以为各种用户事件添加处理程序(例如,最小化或最大化窗口)。

¥Because the BrowserWindow module is an EventEmitter, you can also add handlers for various user events (for example, minimizing or maximizing your window).

BrowserWindow 实例被销毁时,其相应的渲染器进程也会终止。

¥When a BrowserWindow instance is destroyed, its corresponding renderer process gets terminated as well.

应用生命周期

¥Application lifecycle

主进程还通过 Electron 的 app 模块控制应用的生命周期。此模块提供了大量事件和方法,你可以使用它们来添加自定义应用行为(例如,以编程方式退出应用、修改应用停靠栏或显示“关于”面板)。

¥The main process also controls your application's lifecycle through Electron's app module. This module provides a large set of events and methods that you can use to add custom application behavior (for instance, programmatically quitting your application, modifying the application dock, or showing an About panel).

作为一个实际示例,快速入门指南 中显示的应用使用 app API 来创建更原生的应用窗口体验。

¥As a practical example, the app shown in the quick start guide uses app APIs to create a more native application window experience.

main.js
// quitting the app when no windows are open on non-macOS platforms
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})

原生 API

¥Native APIs

为了将 Electron 的功能扩展到 Web 内容的 Chromium 封装器之外,主进程还添加了自定义 API 来与用户的操作系统进行交互。Electron 公开了控制原生桌面功能的各种模块,例如菜单、对话框和托盘图标。

¥To extend Electron's features beyond being a Chromium wrapper for web contents, the main process also adds custom APIs to interact with the user's operating system. Electron exposes various modules that control native desktop functionality, such as menus, dialogs, and tray icons.

有关 Electron 主进程模块的完整列表,请查看我们的 API 文档。

¥For a full list of Electron's main process modules, check out our API documentation.

渲染器进程

¥The renderer process

每个 Electron 应用都会为每个打开的 BrowserWindow(以及每个 Web 嵌入)生成一个单独的渲染器进程。顾名思义,渲染器负责渲染 Web 内容。出于所有意图和目的,渲染器进程中运行的代码的行为应符合 Web 标准(至少 Chromium 是这样)。

¥Each Electron app spawns a separate renderer process for each open BrowserWindow (and each web embed). As its name implies, a renderer is responsible for rendering web content. For all intents and purposes, code ran in renderer processes should behave according to web standards (insofar as Chromium does, at least).

因此,单个浏览器窗口中的所有用户界面和应用功能都应使用你在网络上使用的相同工具和范例来编写。

¥Therefore, all user interfaces and app functionality within a single browser window should be written with the same tools and paradigms that you use on the web.

尽管解释每个网络规范超出了本指南的范围,但至少要理解的是:

¥Although explaining every web spec is out of scope for this guide, the bare minimum to understand is:

  • HTML 文件是渲染器进程的入口点。

    ¥An HTML file is your entry point for the renderer process.

  • UI 样式是通过级联样式表 (CSS) 添加的。

    ¥UI styling is added through Cascading Style Sheets (CSS).

  • 可以通过 <script> 元素添加可执行的 JavaScript 代码。

    ¥Executable JavaScript code can be added through <script> elements.

而且,这也意味着渲染器无法直接访问 require 或其他 Node.js API。为了在渲染器中直接包含 NPM 模块,你必须使用与在 Web 上使用的相同的打包器工具链(例如 webpackparcel)。

¥Moreover, this also means that the renderer has no direct access to require or other Node.js APIs. In order to directly include NPM modules in the renderer, you must use the same bundler toolchains (for example, webpack or parcel) that you use on the web.

警告

可以使用完整的 Node.js 环境生成渲染器进程,以便于开发。从历史上看,这曾经是默认设置,但出于安全原因,此功能被禁用。

¥Renderer processes can be spawned with a full Node.js environment for ease of development. Historically, this used to be the default, but this feature was disabled for security reasons.

此时,你可能想知道渲染器进程用户界面如何与 Node.js 和 Electron 的原生桌面功能交互,如果这些功能只能从主进程访问。事实上,没有直接的方法来导入 Electron 的内容脚本。

¥At this point, you might be wondering how your renderer process user interfaces can interact with Node.js and Electron's native desktop functionality if these features are only accessible from the main process. In fact, there is no direct way to import Electron's content scripts.

预加载脚本

¥Preload scripts

预加载脚本包含在 Web 内容开始加载之前在渲染器进程中执行的代码。这些脚本在渲染器上下文中运行,但通过访问 Node.js API 被授予更多权限。

¥Preload scripts contain code that executes in a renderer process before its web content begins loading. These scripts run within the renderer context, but are granted more privileges by having access to Node.js APIs.

预加载脚本可以附加到 BrowserWindow 构造函数的 webPreferences 选项中的主进程。

¥A preload script can be attached to the main process in the BrowserWindow constructor's webPreferences option.

main.js
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
// ...

由于预加载脚本与渲染器共享全局 Window 接口,并且可以访问 Node.js API,因此它可以通过公开 window 全局中的任意 API 来增强渲染器,然后你的 Web 内容就可以使用这些 API。

¥Because the preload script shares a global Window interface with the renderers and can access Node.js APIs, it serves to enhance your renderer by exposing arbitrary APIs in the window global that your web contents can then consume.

尽管预加载脚本与其附加的渲染器共享 window 全局变量,但由于 contextIsolation 默认值,你无法直接将预加载脚本中的任何变量附加到 window

¥Although preload scripts share a window global with the renderer they're attached to, you cannot directly attach any variables from the preload script to window because of the contextIsolation default.

preload.js
window.myAPI = {
desktop: true
}
renderer.js
console.log(window.myAPI)
// => undefined

上下文隔离意味着预加载脚本与渲染器的主要世界隔离,以避免将任何特权 API 泄漏到 Web 内容的代码中。

¥Context Isolation means that preload scripts are isolated from the renderer's main world to avoid leaking any privileged APIs into your web content's code.

相反,使用 contextBridge 模块来安全地完成此操作:

¥Instead, use the contextBridge module to accomplish this securely:

preload.js
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
desktop: true
})
renderer.js
console.log(window.myAPI)
// => { desktop: true }

此功能对于两个主要目的非常有用:

¥This feature is incredibly useful for two main purposes:

  • 通过向渲染器公开 ipcRenderer 辅助程序,你可以使用进程间通信 (IPC) 从渲染器触发主进程任务(反之亦然)。

    ¥By exposing ipcRenderer helpers to the renderer, you can use inter-process communication (IPC) to trigger main process tasks from the renderer (and vice-versa).

  • 如果你正在为远程 URL 上托管的现有 Web 应用开发 Electron 封装器,则可以将自定义属性添加到渲染器的 window 全局中,该全局属性可用于 Web 客户端上的仅桌面逻辑。

    ¥If you're developing an Electron wrapper for an existing web app hosted on a remote URL, you can add custom properties onto the renderer's window global that can be used for desktop-only logic on the web client's side.

实用进程

¥The utility process

每个 Electron 应用都可以使用 UtilityProcess API 从主进程生成多个子进程。该实用程序进程在 Node.js 环境中运行,这意味着它能够使用 require 模块并使用所有 Node.js API。该实用程序进程可用于托管例如:不受信任的服务、CPU 密集型任务或容易崩溃的组件,这些组件以前托管在主进程或使用 Node.js child_process.fork API 生成的进程中。实用程序进程与 Node.js child_process 模块生成的进程之间的主要区别在于,实用程序进程可以使用 MessagePort 与渲染器进程建立通信通道。当需要从主进程派生子进程时,Electron 应用总是更喜欢 UtilityProcess API 而不是 Node.js child_process.fork API。

¥Each Electron app can spawn multiple child processes from the main process using the UtilityProcess API. The utility process runs in a Node.js environment, meaning it has the ability to require modules and use all of Node.js APIs. The utility process can be used to host for example: untrusted services, CPU intensive tasks or crash prone components which would have previously been hosted in the main process or process spawned with Node.js child_process.fork API. The primary difference between the utility process and process spawned by Node.js child_process module is that the utility process can establish a communication channel with a renderer process using MessagePorts. An Electron app can always prefer the UtilityProcess API over Node.js child_process.fork API when there is need to fork a child process from the main process.

特定于进程的模块别名 (TypeScript)

¥Process-specific module aliases (TypeScript)

Electron 的 npm 包还导出包含 Electron TypeScript 类型定义子集的子路径。

¥Electron's npm package also exports subpaths that contain a subset of Electron's TypeScript type definitions.

  • electron/main 包括所有主要过程模块的类型。

    ¥electron/main includes types for all main process modules.

  • electron/renderer 包括所有渲染器进程模块的类型。

    ¥electron/renderer includes types for all renderer process modules.

  • electron/common 包括可以在主进程和渲染器进程中运行的模块的类型。

    ¥electron/common includes types for modules that can run in main and renderer processes.

这些别名对运行时没有影响,但可用于类型检查和自动补齐。

¥These aliases have no impact on runtime, but can be used for typechecking and autocomplete.

Usage example
const { app } = require('electron/main')
const { shell } = require('electron/common')