javascriptnode.jsvue.jselectronelectron-vue

Sending messages through Electron IPCmain channel to vue instance only works one way


I'm tyring to send messages back and forth between the main electron process and the vue instance. What i have so far is

Preload.js:

import { contextBridge, ipcRenderer } from 'electron'
window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
        const element = document.getElementById(selector)
        if (element) element.innerText = text
    }

    for (const dependency of ['chrome', 'node', 'electron']) {
        replaceText(`${dependency}-version`, process.versions[dependency])
    }

})
window.ipcRenderer = require('electron').ipcRenderer;

contextBridge.exposeInMainWorld('ipcRenderer', {
    //Render (Vue) to main (Electron)
    send: (channel, data) => {
        let validChannels = ['clientMessage'] // <-- Array of all ipcRenderer Channels used in the client
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, data)
        }
    },
    //Main (Electron) to Render (Vue)
    on: (channel, func) => {
        let validChannels = ['electronMessage'] // <-- Array of all ipcMain Channels used in the electron
        if (validChannels.includes(channel)) {
            // Deliberately strip event as it includes `sender`
            ipcRenderer.on(channel, (event, ...args) => func(...args))
        }
    }
})

So i have 2 whitelisted channels in ipcRender, one is called 'clientMessage' to send messaged from the vue instance to the electron main process, the other is 'electronMessage' to send messages from the electron main process to the vue instance.

In my background.js i have the following:

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

ipcMain.on('clientMessage', (event, args) => {
 console.log('received a message from vue: '+args)
  event.sender.send('message','return Message from electron'+args);
});

And in my app.Vue i have:

mounted () {
    window.ipcRenderer.on('electronMessage', (event, data) => {
      console.log('message from electron: '+data)
    })
  },
  methods: {
    sendMessage(){
      window.ipcRenderer.send('clientMessage','testing')
    }
  }

The app runs fine, and when i call the sendMessage function I correctly get a console log on the electron terminal saying message received. So clearly vue -> electron messaging has worked, but why wont it work for the reverse?


Solution

  • I figured out why, the way to send messages from electron main process to the renderer instance is not the same. Since there is only one electron main process running at all times, we can simple do window.ipcRenderer.send('', 'your message') in the vue renderer instance, but since there can be more than one renderer instances running, the main electron process needs to know which render process to send it to.

    So in your main.js/background.js whatever .js you are running the electron main process out of. do this:

    let win;
    
    async function createWindow() {
      // Create the browser window.
      win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          nodeIntegration: false,
          contextIsolation: true,
          enableRemoteModule: true,
          preload: path.join(__dirname, "preload.js")
        }
      })
    .
    .
    .
    

    and then to send a message to the render instance:

    win.webContents.send('<channelName>','your message')
    

    that way it sends it to the correct render process. So my code in the end looks like this:

    ipcMain.on('clientMessage', (event, args) => {
     console.log('received a message from vue: '+args)
     win.webContents.send('electronMessage','Reply from main process: '+args)
    });