* docs: fixed typos * Update docs/tutorial/performance.md Co-authored-by: Keeley Hammond <vertedinde@electronjs.org> * Update performance.md --------- Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
		
			
				
	
	
		
			254 lines
		
	
	
	
		
			10 KiB
			
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
	
		
			10 KiB
			
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						|
title: 'Process Model'
 | 
						|
description: '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.'
 | 
						|
slug: process-model
 | 
						|
hide_title: false
 | 
						|
---
 | 
						|
 | 
						|
# Process Model
 | 
						|
 | 
						|
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][tutorial].
 | 
						|
 | 
						|
[tutorial]: ./tutorial-1-prerequisites.md
 | 
						|
 | 
						|
## Why not a single process?
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|

 | 
						|
 | 
						|
Electron applications are structured very similarly. As an app developer, you control
 | 
						|
two types of processes: [main](#the-main-process) and [renderer](#the-renderer-process).
 | 
						|
These are analogous to Chrome's own browser and renderer processes outlined above.
 | 
						|
 | 
						|
[chrome comic]: https://www.google.com/googlebooks/chrome/
 | 
						|
 | 
						|
## The main process
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
The main process' primary purpose is to create and manage application windows with the
 | 
						|
[`BrowserWindow`][browser-window] module.
 | 
						|
 | 
						|
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`][web-contents] object.
 | 
						|
 | 
						|
```js title='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)
 | 
						|
```
 | 
						|
 | 
						|
> Note: A renderer process is also created for [web embeds][web-embed] such as the
 | 
						|
> `BrowserView` module. The `webContents` object is also accessible for embedded
 | 
						|
> web content.
 | 
						|
 | 
						|
Because the `BrowserWindow` module is an [`EventEmitter`][event-emitter], you can also
 | 
						|
add handlers for various user events (for example, minimizing or maximizing your window).
 | 
						|
 | 
						|
When a `BrowserWindow` instance is destroyed, its corresponding renderer process gets
 | 
						|
terminated as well.
 | 
						|
 | 
						|
[browser-window]: ../api/browser-window.md
 | 
						|
[web-embed]: ../tutorial/web-embeds.md
 | 
						|
[web-contents]: ../api/web-contents.md
 | 
						|
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
 | 
						|
 | 
						|
### Application lifecycle
 | 
						|
 | 
						|
The main process also controls your application's lifecycle through Electron's
 | 
						|
[`app`][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).
 | 
						|
 | 
						|
As a practical example, the app shown in the [quick start guide][quick-start-lifecycle]
 | 
						|
uses `app` APIs to create a more native application window experience.
 | 
						|
 | 
						|
```js title='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()
 | 
						|
})
 | 
						|
```
 | 
						|
 | 
						|
[app]: ../api/app.md
 | 
						|
[quick-start-lifecycle]: ../tutorial/quick-start.md#manage-your-windows-lifecycle
 | 
						|
 | 
						|
### Native APIs
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
For a full list of Electron's main process modules, check out our API documentation.
 | 
						|
 | 
						|
## The renderer process
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
- An HTML file is your entry point for the renderer process.
 | 
						|
- UI styling is added through Cascading Style Sheets (CSS).
 | 
						|
- Executable JavaScript code can be added through `<script>` elements.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
:::warning
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
:::
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
 | 
						|
change the statements here. -->
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
A preload script can be attached to the main process in the `BrowserWindow` constructor's
 | 
						|
`webPreferences` option.
 | 
						|
 | 
						|
```js title='main.js'
 | 
						|
const { BrowserWindow } = require('electron')
 | 
						|
// ...
 | 
						|
const win = new BrowserWindow({
 | 
						|
  webPreferences: {
 | 
						|
    preload: 'path/to/preload.js'
 | 
						|
  }
 | 
						|
})
 | 
						|
// ...
 | 
						|
```
 | 
						|
 | 
						|
Because the preload script shares a global [`Window`][window-mdn] 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.
 | 
						|
 | 
						|
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`][context-isolation] default.
 | 
						|
 | 
						|
```js title='preload.js' @ts-nocheck
 | 
						|
window.myAPI = {
 | 
						|
  desktop: true
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
```js title='renderer.js' @ts-nocheck
 | 
						|
console.log(window.myAPI)
 | 
						|
// => undefined
 | 
						|
```
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
Instead, use the [`contextBridge`][context-bridge] module to accomplish this
 | 
						|
securely:
 | 
						|
 | 
						|
```js title='preload.js'
 | 
						|
const { contextBridge } = require('electron')
 | 
						|
 | 
						|
contextBridge.exposeInMainWorld('myAPI', {
 | 
						|
  desktop: true
 | 
						|
})
 | 
						|
```
 | 
						|
 | 
						|
```js title='renderer.js' @ts-nocheck
 | 
						|
console.log(window.myAPI)
 | 
						|
// => { desktop: true }
 | 
						|
```
 | 
						|
 | 
						|
This feature is incredibly useful for two main purposes:
 | 
						|
 | 
						|
- By exposing [`ipcRenderer`][ipcrenderer] helpers to the renderer, you can use
 | 
						|
  inter-process communication (IPC) to trigger main process tasks from the
 | 
						|
  renderer (and vice-versa).
 | 
						|
- 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
 | 
						|
 | 
						|
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 [`MessagePort`][]s. 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.
 | 
						|
 | 
						|
## Process-specific module aliases (TypeScript)
 | 
						|
 | 
						|
Electron's npm package also exports subpaths that contain a subset of
 | 
						|
Electron's TypeScript type definitions.
 | 
						|
 | 
						|
- `electron/main` includes types for all main process modules.
 | 
						|
- `electron/renderer` includes types for all renderer process modules.
 | 
						|
- `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.
 | 
						|
 | 
						|
```js title="Usage example"
 | 
						|
const { app } = require('electron/main')
 | 
						|
const { shell } = require('electron/common')
 | 
						|
```
 | 
						|
 | 
						|
[window-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window
 | 
						|
[`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
 | 
						|
[`child_process.fork`]: https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_processforkmodulepath-args-options
 | 
						|
[context-isolation]: ./context-isolation.md
 | 
						|
[context-bridge]: ../api/context-bridge.md
 | 
						|
[ipcrenderer]: ../api/ipc-renderer.md
 | 
						|
[UtilityProcess]: ../api/utility-process.md
 |