From 890adefb959b4f8923b92d971785259d1865d5f5 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Fri, 29 Jul 2022 11:44:45 -0700 Subject: [PATCH] docs: new main -> renderers messageChannel example (#35133) * 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 * update code example headings, reference contextIsolation example * remove nodeIntegration: false from browserWindows * rename "messagePort" to "electronMessagePort" for compatibility Co-authored-by: Kilian Valkhof Co-authored-by: Erick Zhao --- docs/tutorial/message-ports.md | 103 ++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/docs/tutorial/message-ports.md b/docs/tutorial/message-ports.md index 9f1e9bf467aff..b236f4133e450 100644 --- a/docs/tutorial/message-ports.md +++ b/docs/tutorial/message-ports.md @@ -8,8 +8,7 @@ your app. Here is a very brief example of what a MessagePort is and how it works: -```js -// renderer.js /////////////////////////////////////////////////////////////// +```js title='renderer.js (Renderer Process)' // MessagePorts are created in pairs. A connected pair of message ports is // called a channel. const channel = new MessageChannel() @@ -28,8 +27,7 @@ port2.postMessage({ answer: 42 }) ipcRenderer.postMessage('port', null, [port1]) ``` -```js -// main.js /////////////////////////////////////////////////////////////////// +```js title='main.js (Main Process)' // In the main process, we receive the port. ipcMain.on('port', (event) => { // 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 +### 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 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 process, without the performance overhead of relaying via the main process. -```js -// main.js /////////////////////////////////////////////////////////////////// +```js title='main.js (Main Process)' const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron') app.whenReady().then(async () => { @@ -129,8 +197,7 @@ app.whenReady().then(async () => { }) ``` -```html - +```html title='worker.html' ``` -```html - +```html title='app.html'