После тестирования в официальном демо при установке contextIsolation
как true
в конфигурационном файле config.default.js
, а также активации preload: path.join(appInfo.baseDir, 'preload', 'bridge.js')
, возникли ошибки при вызове метода removeAllListeners
в файле ./frontend/src/utils/ipcRenderer.js
. После сравнения поведения с включенными и отключенными режимами контекста было установлено, что официальная документация Electron не рекомендует прямое использование ipcRenderer
при включённой изоляции контекста. В действительности, это ограничение уже встроено внутрь системы.
Мой файл bridge.js
был модифицирован следующим образом, чтобы успешно воспроизвести поведение ipcRenderer
без изоляции контекста:
const { contextBridge, ipcRenderer } = require('electron');
// Пример передачи данных через bridge
contextBridge.exposeInMainWorld('ipcRenderer', {
send(channel, data) {
ipcRenderer.send(channel, data);
},
on(channel, func) {
const handler = (event) => func(event);
ipcRenderer.on(channel, handler);
return () => {
ipcRenderer.removeListener(channel, handler);
};
},
removeAllListeners() {
ipcRenderer.removeAllListeners();
}
});
```Этот код позволяет безопасно использовать `ipcRenderer` даже при включенном режиме изоляции контекста.
```javascript
const { contextBridge, ipcRenderer } = require('electron');
/**
* Официальная документация ipc: https://www.electronjs.org/ru/docs/latest/api/ipc-renderer
*
* Свойства/Методы
* ipc.on(channel, listener) - Отслеживает channel, когда приходит новое сообщение, вызывает listener (альтернативное имя для ipcRenderer.addListener)
* ipc.once(channel, listener) - Добавляет одноразовый listener
* ipc.off(channel, listener) - Удаляет конкретный listener для определенного channel из очереди слушателей (альтернативное имя для ipcRenderer.removeListener)
* ipc.removeAllListeners(channel) - Удаляет все слушатели, если указан channel, то удаляются только те, что связаны с ним
* ipc.send(channel, ...args) - Отправляет асинхронное сообщение через channel в основной процесс
* ipc.invoke(channel, param) - Отправляет асинхронное сообщение (модель invoke/handle)
* Нечасто используемые методы
* ipc.postMessage(channel, message, [transfer]) - Отправляет сообщение в основной процесс
* ipc.sendSync(channel, param) - Отправляет синхронное сообщение (модель send/on), использование не рекомендуется
* ipc.sendToHost(channel, ...args) - Сообщение отправляется в элемент <webview> на странице host
* Устаревшие методы
* ipc.sendTo(webContentsId, channel, ...args) - Отправляет сообщение через channel в окно с webContentsId
*
* Отправка сообщений от основного процесса в процесс рендера (три способа)
* При использовании on, параметр event предоставляет event.reply и event.sender.
*/
```отправить, первый отправляет ответ обратно отправителю (может быть частью страницы), второй отправляет сообщение в процесс
* При использовании handle, можно вернуть значение через async, например ipcMain. handle("isTablePC", async (event, data) => { return isTabletPC });
* Также можно использовать метод webContents. send основного процесса для отправки сообщений
*
* Электрон-эгг фреймворк по умолчанию поддерживает каналы с названием контроллер. файл. метод, так как они уже прослушиваются в ee-core/socket/ipcServer. js!
*/
``````javascript
// const { on, off, once, removeAllListeners, send, invoke, postMessage, sendToHost, sendTo } = ipcRenderer;```javascript
contextBridge.exposeInMainWorld('electron', {
// Ошибка 1: Электрон официально не рекомендует прямое использование ipcRenderer, поэтому использование ipcRenderer в приложении может вызывать проблемы с некоторыми методами
// ipcRenderer,
// Ошибка 2: Хотелось распаковать и переопределить методы, но это привело к тому, что некоторые методы работали некорректно, например window.electron.ipcRenderer.on не работает как ожидалось
// ipcRenderer:{on, off, once, removeAllListeners, send, invoke, postMessage, sendToHost, sendTo},//Хотелось использовать деконструкцию
// Корректное решение: каждый метод должен быть переопределен отдельно, чтобы они корректно работали в контексте рендера
ipcRenderer: {
on: (channel, listener) => ipcRenderer.on(channel, listener),//Подписаться на событие
once: (channel, listener) => ipcRenderer.once(channel, listener),//Подписаться на событие один раз
off: (channel, listener) => ipcRenderer.removeListener(channel, listener),//Отписаться от события
removeAllListeners: (channel) => ipcRenderer.removeAllListeners(channel),//Удалить все подписки на канал
send: (channel, ...args) => ipcRenderer.send(channel, ...args),//Отправить сообщение (асинхронно)
// sendSync: (channel, params) => ipcRenderer.sendSync(channel, params),//Отправить сообщение (синхронно), Электрон официально считает этот способ устаревшим и не рекомендует его
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),//Вызвать асинхронную функцию (через Promise), Электрон рекомендует этот способ
postMessage: (channel, message, [transfer]) => ipcRenderer.postMessage(channel, message, [transfer]),//Отправить сообщение в основной процесс
sendToHost: (channel, ...args) => ipcRenderer.sendToHost(channel, ...
Пожалуйста, обратите внимание, что последняя строка была оборвана и требует завершения.```javascript
args), //Аналогично send, отличие в том, что отправляет сообщение в webview элемент хоста
sendTo: (webContentsId, channel, ...args) => ipcRenderer.sendTo(webContentsId, channel, ...args), //Отправить сообщение в указанный контент
},
});