Отправка сообщения от Main к Renderer

Может ли кто-нибудь помочь мне здесь. Я полностью запутался в том, как решить эту проблему. Я потратил около недели, пытаясь найти решение для этого, но потерпел неудачу, и, похоже, в Интернете отсутствует надежное решение. Я сделал репозиторий github, пытаясь продемонстрировать проблему.

Короче говоря, я реализовал строку состояния в своем приложении, которую я хочу заполнить различными строковыми сообщениями. Эти сообщения будут отправляться из функций, содержащихся в js-файле, который импортирует электрон, что означает, что он не имеет прямого доступа к рендереру. Итак, как я могу отправить эти сообщения в Renderer. Я предполагаю, что это нужно сделать с помощью ContextBridge, но я понятия не имею, как это сделать успешно, поэтому, если ваш ответ просто связывает меня с документами контекстного моста, не беспокойтесь, лол, я утомил себя поиском при этом. Другой альтернативой, которую я рассматривал, является использование пользовательского события, но я не уверен, что это тоже решит проблему.

Вот пример того, что я пытаюсь сделать вместе с репозиторием на github. Если вы сделаете запрос на внесение изменений, чтобы исправить репозиторий, я с удовольствием объединим его и оставлю репозиторий общедоступным, чтобы другие могли извлечь из него пользу и поделиться им с сообществом. https://github.com/JokerMartini/statusbar

В качестве небольшой проблемы я не уверен, почему я больше не могу вызывать getPath из «приложения» из файла js, который не загружен в поток рендеринга.

Я запускаю метод из Renderer

index.vue

const doWork = () => {
    window.messenger.doWork();
}

электронная предварительная загрузка.js

import { contextBridge } from "electron";

const messenger = require("../src/helpers/messenger");
contextBridge.exposeInMainWorld("messenger", messenger);

мессенджер.js

const { app } = require("electron");
const path = require("path");

// using electron module to demonstrate this file can't be imported into renderer
export function showMessage(msg) {
  const dir = path.join(app.getPath("documents"), "presets");
  console.info(dir);
  // TODO: send message to renderer...
}

export function doWork() {
  console.info("Doing working...");

  // step 1: long process
  showMessage("Processing step 1...");

  // step 2: long process
  showMessage("Processing step 2...");

  // step 3: long process
  showMessage("Processing step 3...");
}

Я хотел бы, чтобы сообщения, отправленные с основного средства визуализации, отображались в строке состояния

main.vue

<q-footer>
     <q-bar>
        <span class = "text-caption">Show message here...</span>
    </q-bar>
</q-footer>

** ОБНОВЛЕНИЕ 01 **

По какой-то причине мое сообщение не принимается в Renderer. Вот мои изменения кода

электронная предварительная загрузка.js

import { contextBridge, ipcRenderer } from "electron";

contextBridge.exposeInMainWorld("electronAPI", {
  setStatus: (callback, func) =>
    ipcRenderer.on("set-status", (event, ...args) => func(...args)),
});

index.vue

<template>
  <q-page class = "flex flex-center">
    <q-btn label = "Show Message" @click = "doWork" />
  </q-page>
</template>

<script>
import { defineComponent } from "vue";

export default defineComponent({

  setup() {
    // send message for testing...
    const doWork = () => {
      window.electronAPI.setStatus("sfsfsdfsd");
    };

    // recieve status messages...
    window.electronAPI.setStatus("set-status", (data) => {
      console.info("STATUS:", data);
      // TODO $store.dispatch("....");
    });

    return {
      doWork,
    };
  },
});
</script>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы хотите использовать ipcMain (в основном процессе) и ipcRenderer (в процессе рендеринга). Если вы сравните свои скрипты с примерами на https://www.electronjs.org/docs/latest/tutorial/ipc, это то, чего не хватает.

(Есть раздел, посвященный основному рендереру.)

Раньше это было яснее и проще, но давало больше возможностей для злоупотреблений. Поэтому лучше всего игнорировать любые онлайн-уроки старше года или около того. (Хотя contextBridge довольно новый, поэтому, если они упоминают об этом, они должны быть последними.)

Спасибо, Даррен, ваше предложение немного помогло. Я чувствую, что я очень близко. Я обновил свой ответ выше, если вы вообще можете мне помочь, поскольку вы, кажется, лучше понимаете.

JokerMartini 22.03.2022 13:34
Ответ принят как подходящий

В моем случае работает метод нет для использования скрипта preload.js для определения конкретных реализаций. Вместо этого я использую скрипт preload.js, чтобы определить только канал (имена), с которым я могу общаться между основным потоком и потоком рендеринга. IE: Разделяю ваши опасения. Реализуйте свои конкретные функции в сценариях основных потоков и сценариях потоков рендеринга.


preload.js

// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// White-listed channels.
const ipc = {
    'render': {
        // From render to main.
        'send': [],
        // From main to render.
        'receive': [
            'message:update' // Here is your channel name
        ],
        // From render to main and back again.
        'sendReceive': []
    }
};

// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods.
    'ipcRender', {
        // From render to main.
        send: (channel, args) => {
            let validChannels = ipc.render.send;
            if (validChannels.includes(channel)) {
                ipcRenderer.send(channel, args);
            }
        },
        // From main to render.
        receive: (channel, listener) => {
            let validChannels = ipc.render.receive;
            if (validChannels.includes(channel)) {
                // Deliberately strip event as it includes `sender`.
                ipcRenderer.on(channel, (event, ...args) => listener(...args));
            }
        },
        // From render to main and back again.
        invoke: (channel, args) => {
            let validChannels = ipc.render.sendReceive;
            if (validChannels.includes(channel)) {
                return ipcRenderer.invoke(channel, args);
            }
        }
    }
);

Note: Though I do not use Vue.js, you should get the gist of the below two files.

main.js (основная тема)

const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;

const nodePath = require("path");

let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true, 
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile('index.html')
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
    
    // Send a message to the window.
    window.webContents.send('message:update', 'Doing work...');
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

index.html (рендерить нить)

<!DOCTYPE html>
<html lang = "en">
    <head>
        <meta charset = "UTF-8">
        <title>Title</title>
    </head>
    
    <body>
        <span id = "text-caption">Show message here...</span>
    </body>
    
    <script>      
        // Listen for message updates from the main thread.
        window.ipcRender.receive('message:update', (message) => {
            document.getElementById('text-caption').innerText = message;
        });
    </script>
</html>

Это отличная разбивка @midnight-coding Мне было любопытно узнать, как я могу отправлять сообщения из других файлов js, которые могут работать на main, поскольку веб-контент недоступен? Я позвоню ipcRenderer.send("message:update", "hello world")

JokerMartini 22.03.2022 14:35

@JokerMartini Переместите создание вашего окна в отдельный файл. В этом файле добавьте функцию get(), чтобы вернуть объект окна. ИИ: function get() { return window; }. Затем в любом файле, который в этом нуждается, require файл и вызов get(). НАГ: appMainWindow.get().webContents.send('message:update', 'hello world');. См. этот ответ для более подробного объяснения.

midnight-coding 22.03.2022 14:49

Можно ли использовать метод ipcRenderer.send? Или это для чего-то другого?

JokerMartini 22.03.2022 16:36

@JokerMartini Извините, я неправильно понял ваш вопрос. Вроде... В скрипте preload.js команда ipcRenderer.send(...) настроена под ключ ipcRender.send, поэтому в файлах скрипта .js потока рендеринга вы можете вызывать window.ipcRender.send('channel:name', 'This is my data to send to the main thread');. Используя этот preload.js-скрипт, не нужно будет пытаться напрямую использовать (в той или иной форме) ipcRenderer.send(...), поскольку он реализован через preload.js-скрипт. Это отвечает на ваш вопрос комментария?

midnight-coding 22.03.2022 22:27

Другие вопросы по теме