docs: new main -> renderers messageChannel example (#34978)
* docs: new main -> renderers messageChannel example * consistent use of your * fix a typo * linting * markdown linting * Update docs/tutorial/message-ports.md Co-authored-by: Erick Zhao <erick@hotmail.ca> * update code example headings, reference contextIsolation example * remove nodeIntegration: false from browserWindows * rename "messagePort" to "electronMessagePort" for compatibility Co-authored-by: Erick Zhao <erick@hotmail.ca>
This commit is contained in:
parent
9028bb79a8
commit
00e9bf107a
1 changed files with 81 additions and 22 deletions
|
@ -8,8 +8,7 @@ your app.
|
||||||
|
|
||||||
Here is a very brief example of what a MessagePort is and how it works:
|
Here is a very brief example of what a MessagePort is and how it works:
|
||||||
|
|
||||||
```js
|
```js title='renderer.js (Renderer Process)'
|
||||||
// renderer.js ///////////////////////////////////////////////////////////////
|
|
||||||
// MessagePorts are created in pairs. A connected pair of message ports is
|
// MessagePorts are created in pairs. A connected pair of message ports is
|
||||||
// called a channel.
|
// called a channel.
|
||||||
const channel = new MessageChannel()
|
const channel = new MessageChannel()
|
||||||
|
@ -28,8 +27,7 @@ port2.postMessage({ answer: 42 })
|
||||||
ipcRenderer.postMessage('port', null, [port1])
|
ipcRenderer.postMessage('port', null, [port1])
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js title='main.js (Main Process)'
|
||||||
// main.js ///////////////////////////////////////////////////////////////////
|
|
||||||
// In the main process, we receive the port.
|
// In the main process, we receive the port.
|
||||||
ipcMain.on('port', (event) => {
|
ipcMain.on('port', (event) => {
|
||||||
// When we receive a MessagePort in the main process, it becomes a
|
// When we receive a MessagePort in the main process, it becomes a
|
||||||
|
@ -84,14 +82,84 @@ process, you can listen for the `close` event by calling `port.on('close',
|
||||||
|
|
||||||
## Example use cases
|
## Example use cases
|
||||||
|
|
||||||
|
### Setting up a MessageChannel between two renderers
|
||||||
|
|
||||||
|
In this example, the main process sets up a MessageChannel, then sends each port
|
||||||
|
to a different renderer. This allows renderers to send messages to each other
|
||||||
|
without needing to use the main process as an in-between.
|
||||||
|
|
||||||
|
```js title='main.js (Main Process)'
|
||||||
|
const { BrowserWindow, app, MessageChannelMain } = require('electron')
|
||||||
|
|
||||||
|
app.whenReady().then(async () => {
|
||||||
|
// create the windows.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: false,
|
||||||
|
preload: 'preloadMain.js'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const secondaryWindow = BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
contextIsolation: false,
|
||||||
|
preload: 'preloadSecondary.js'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// set up the channel.
|
||||||
|
const { port1, port2 } = new MessageChannelMain()
|
||||||
|
|
||||||
|
// once the webContents are ready, send a port to each webContents with postMessage.
|
||||||
|
mainWindow.once('ready-to-show', () => {
|
||||||
|
mainWindow.webContents.postMessage('port', null, [port1])
|
||||||
|
})
|
||||||
|
|
||||||
|
secondaryWindow.once('ready-to-show', () => {
|
||||||
|
secondaryWindow.webContents.postMessage('port', null, [port2])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, in your preload scripts you receive the port through IPC and set up the
|
||||||
|
listeners.
|
||||||
|
|
||||||
|
```js title='preloadMain.js and preloadSecondary.js (Preload scripts)'
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
ipcRenderer.on('port', e => {
|
||||||
|
// port received, make it globally available.
|
||||||
|
window.electronMessagePort = e.ports[0]
|
||||||
|
|
||||||
|
window.electronMessagePort.onmessage = messageEvent => {
|
||||||
|
// handle message
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example messagePort is bound to the `window` object directly. It is better
|
||||||
|
to use `contextIsolation` and set up specific contextBridge calls for each of your
|
||||||
|
expected messages, but for the simplicity of this example we don't. You can find an
|
||||||
|
example of context isolation further down this page at [Communicating directly between the main process and the main world of a context-isolated page](#communicating-directly-between-the-main-process-and-the-main-world-of-a-context-isolated-page)
|
||||||
|
|
||||||
|
That means window.messagePort is globally available and you can call
|
||||||
|
`postMessage` on it from anywhere in your app to send a message to the other
|
||||||
|
renderer.
|
||||||
|
|
||||||
|
```js title='renderer.js (Renderer Process)'
|
||||||
|
// elsewhere in your code to send a message to the other renderers message handler
|
||||||
|
window.electronMessagePort.postmessage('ping')
|
||||||
|
```
|
||||||
|
|
||||||
### Worker process
|
### Worker process
|
||||||
|
|
||||||
In this example, your app has a worker process implemented as a hidden window.
|
In this example, your app has a worker process implemented as a hidden window.
|
||||||
You want the app page to be able to communicate directly with the worker
|
You want the app page to be able to communicate directly with the worker
|
||||||
process, without the performance overhead of relaying via the main process.
|
process, without the performance overhead of relaying via the main process.
|
||||||
|
|
||||||
```js
|
```js title='main.js (Main Process)'
|
||||||
// main.js ///////////////////////////////////////////////////////////////////
|
|
||||||
const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron')
|
const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron')
|
||||||
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
|
@ -129,8 +197,7 @@ app.whenReady().then(async () => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html title='worker.html'
|
||||||
<!-- worker.html ------------------------------------------------------------>
|
|
||||||
<script>
|
<script>
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
@ -153,8 +220,7 @@ ipcRenderer.on('new-client', (event) => {
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html title='app.html'
|
||||||
<!-- app.html --------------------------------------------------------------->
|
|
||||||
<script>
|
<script>
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
@ -182,9 +248,7 @@ Electron's built-in IPC methods only support two modes: fire-and-forget
|
||||||
can implement a "response stream", where a single request responds with a
|
can implement a "response stream", where a single request responds with a
|
||||||
stream of data.
|
stream of data.
|
||||||
|
|
||||||
```js
|
```js title='renderer.js (Renderer Process)'
|
||||||
// renderer.js ///////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
const makeStreamingRequest = (element, callback) => {
|
const makeStreamingRequest = (element, callback) => {
|
||||||
// MessageChannels are lightweight--it's cheap to create a new one for each
|
// MessageChannels are lightweight--it's cheap to create a new one for each
|
||||||
// request.
|
// request.
|
||||||
|
@ -213,9 +277,7 @@ makeStreamingRequest(42, (data) => {
|
||||||
// We will see "got response data: 42" 10 times.
|
// We will see "got response data: 42" 10 times.
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js title='main.js (Main Process)'
|
||||||
// main.js ///////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ipcMain.on('give-me-a-stream', (event, msg) => {
|
ipcMain.on('give-me-a-stream', (event, msg) => {
|
||||||
// The renderer has sent us a MessagePort that it wants us to send our
|
// The renderer has sent us a MessagePort that it wants us to send our
|
||||||
// response over.
|
// response over.
|
||||||
|
@ -242,8 +304,7 @@ the renderer are delivered to the isolated world, rather than to the main
|
||||||
world. Sometimes you want to deliver messages to the main world directly,
|
world. Sometimes you want to deliver messages to the main world directly,
|
||||||
without having to step through the isolated world.
|
without having to step through the isolated world.
|
||||||
|
|
||||||
```js
|
```js title='main.js (Main Process)'
|
||||||
// main.js ///////////////////////////////////////////////////////////////////
|
|
||||||
const { BrowserWindow, app, MessageChannelMain } = require('electron')
|
const { BrowserWindow, app, MessageChannelMain } = require('electron')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
@ -278,8 +339,7 @@ app.whenReady().then(async () => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js title='preload.js (Preload Script)'
|
||||||
// preload.js ////////////////////////////////////////////////////////////////
|
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
// We need to wait until the main world is ready to receive the message before
|
// We need to wait until the main world is ready to receive the message before
|
||||||
|
@ -297,8 +357,7 @@ ipcRenderer.on('main-world-port', async (event) => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html title='index.html'
|
||||||
<!-- index.html ------------------------------------------------------------->
|
|
||||||
<script>
|
<script>
|
||||||
window.onmessage = (event) => {
|
window.onmessage = (event) => {
|
||||||
// event.source === window means the message is coming from the preload
|
// event.source === window means the message is coming from the preload
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue