为啥我的 ipcMain 没有发送到 Electron 中的 ipcRenderer?

Posted

技术标签:

【中文标题】为啥我的 ipcMain 没有发送到 Electron 中的 ipcRenderer?【英文标题】:Why is my ipcMain not sending to ipcRenderer in Electron?为什么我的 ipcMain 没有发送到 Electron 中的 ipcRenderer? 【发布时间】:2019-08-11 11:36:09 【问题描述】:

电子新手我已经知道如何从 Renderer 发送到 Main 但我正在尝试学习如何从 Main渲染器。在我的研究中,我读到:

IPC send from main process to renderer 并尝试过:

main.js

const  app, ipcMain, Menu   = require('electron')
const appVersion = process.env.npm_package_version
const mainWindow = require('./renderer/mainWindow')

app.on('ready', () => 
  mainWindow.createWindow(),
  console.log(`Trying to send app version to renderer: $appVersion`),
  mainWindow.webContents.send('app-version', appVersion),  
  Menu.setApplicationMenu(mainMenu)
)

但我收到以下错误:

Uncaught Exception TypeError Cannot read property 'send' of undefined

读完“Send sync message from IpcMain to IpcRenderer - Electron”后我试了一下:

ipcMain.on('app-version', (event) => 
    console.log(`Sent: $appVersion`)
    event.sender.send(appVersion)
),

但没有任何反应或错误。我的 renderer.js

const  ipcRenderer  = require('electron')
ipcRenderer.on('app-version', (event, res) => 
    console.log(res)
)

为什么我的 ipcMain 没有发送到我的 ipcRenderer

编辑:

mainWindow.js

// Modules
const  BrowserWindow  = require('electron')

// export mainWindow
exports.createWindow = () => 

  // BrowserWindow options
  // https://electronjs.org/docs/api/browser-window#new-browserwindowoptions
  this.win = new BrowserWindow(
      minWidth: 400,
      minHeight: 400,
      frame: false,
      webPreferences: 
        nodeIntegration: true,
        backgroundThrottling: false
      
  )

  // Devtools
  this.win.webContents.openDevTools()

  // Load main window content
  this.win.loadURL(`file://$__dirname/index.html`)

  // Handle window closed
  this.win.on('closed', () => 
    this.win = null
  )

我也试过了:

main.js

app.on('ready', () => 
  mainWindow.createWindow(),
  mainWindow.win.webContents.send('app-version', appVersion),  
  Menu.setApplicationMenu(mainMenu)
)

renderer.js

console.log("Trying")
ipcRenderer.on('app-version', (args) => 
    console.log(`Node version is $args`)
)

由于某种原因,现在我编写的 ipcMain 应用的答案多次发送到渲染器并重复渲染控制台消息

Trying to send app version to renderer: 1.0.0
Trying to send app version to renderer: 1.0.0
Trying to send app version to renderer: 1.0.0
Trying to send app version to renderer: 1.0.0
Trying to send app version to renderer: 1.0.0

现在应用程序闪烁,但在 console.log 中显示一次,我不明白为什么。

编辑

回复answer。

我知道app.getVersion 我只是在使用ENV 来学习如何发送到main。测试代码后,这种方法不起作用。

应对:

app.on('ready', () => 
    const win = mainWindow.createWindow(),
    console.log(`Trying to send app version to renderer: $appVersion`),
    win.webContents.send('app-version', appVersion),  
    Menu.setApplicationMenu(mainMenu)
)

抛出以下错误:

const 声明中缺少初始化程序

如此修改:

app.on('ready', () => 
  const win = mainWindow.createWindow()
  console.log(`Trying to send app version to renderer: $appVersion`)
  win.webContents.send('app-version', appVersion)
  Menu.setApplicationMenu(mainMenu)
)

但是在exports.createWindow末尾添加return时会抛出这个错误:

win 没有定义

exports.createWindow 之前添加let.win 不会抛出任何代码,但不会发送任何内容。


Electron Fiddle

index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Electron learning</title>
    <!-- CSS Bootstrap -->
    <!-- <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css"> -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <!-- 
      Font Awesome 
      https://fontawesome.com/v4.7.0/cheatsheet/ 
    -->
    <!-- <link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.css"> -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">

    <!-- Custom CSS -->
    <style>
      body 
        -webkit-app-region: drag;
      
      footer 
        position: fixed;
        bottom: 0;
        width: 100%;
      
      #close_app,
      #site 
        cursor: pointer;
      
    </style>
  </head>
  <body class="d-flex flex-column h-100">
    <header>
      <nav class="navbar navbar-expand navbar-dark fixed-top bg-dark">
        <a class="navbar-brand text-white">Foobar</a>
        <div class="collapse navbar-collapse justify-content-between">
          <div class="navbar-nav">
            <a id="site" class="nav-link"><small><u id="application"></u></small></a>
          </div>
          <div class="navbar-nav">
              <a id="close_app" class="nav-item nav-link"><i class="fa fa-times-circle"></i></a>
          </div>
        </div>
      </nav>
    </header>

    <main role="main" class="flex-shrink-0">
    <div class="container">
      <h1>Hello World!</h1>
      <!-- All of the Node.js APIs are available in this renderer process. -->
      We are using Node.js <script>document.write(process.versions.node)</script>,
      Chromium <script>document.write(process.versions.chrome)</script>,
      and Electron <script>document.write(process.versions.electron)</script>.
      Electron app version <script>document.write(process.versions.electron)</script>.
    </div>
    <p id="testSender"></p>
    </main>

    <footer class="footer mt-auto py-3 ">
    <div class="container">
      <span class="text-muted">Place sticky footer content here.</span>
    </div>
  </footer>
    <script>
        // jQuery
        // window.jQuery = window.$ = $ = require('jquery')

        // You can also require other files to run in this process
        require('./renderer.js')
    </script>
    <!-- <script src="../node_modules/jquery/dist/jquery.min.js"></script> -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>


    <!-- <script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script> -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </body>
</html>

main.js:

'use strict'

// Modules to control application life and create native browser window
const  app, ipcMain, BrowserWindow, Menu   = require('electron')
const testMainSend = `Trying to send something to renderer`

let mainWindow

// Window state keeper
const windowStateKeeper = require('electron-window-state')

// export mainWindow
function createWindow () 

  let winState = windowStateKeeper(
    defaultWidth: 400,
    defaultHeight: 400
  )

  // BrowserWindow options
  // https://electronjs.org/docs/api/browser-window#new-browserwindowoptions
  const win = new BrowserWindow(
    width: winState.width,
    height: winState.Height,
    x: winState.x,
    y: winState.y,
    minWidth: 400,
    minHeight: 400,
    frame: false,
    webPreferences: 
      nodeIntegration: true,
      backgroundThrottling: false
    
  )

  winState.manage(win)

  // Devtools
  win.webContents.openDevTools()

  // Load main window content
  win.loadURL(`file://$__dirname/index.html`)

  // Handle window closed
  win.on('closed', () => 
      this.win = null
  )

  return win


// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => 
  createWindow()

  // 1st attempt
  // webContents.send('misc-sender', testMainSend)

  // 2nd attempt
  // const win = createWindow()
  // win.webContents.send('misc-sender', testMainSend)

  // 3rd attempt
  const win = createWindow()
  win.webContents.on('dom-ready', () => 
    console.log(`Trying to send renderer: $testMainSend`)
    mainWindow.win.webContents.send('misc-sender', testMainSend)
  )
)

// Quit when all windows are closed.
app.on('window-all-closed', () => 
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') 
    app.quit()
  
)

app.on('activate', () => 
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) 
    createWindow()
  
)

// Close application from button
ipcMain.on('closing-app', () => 
  app.quit()
  console.log('Closed app from font awesome link')
)

renderer.js:

// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.
const  ipcRenderer, shell  = require('electron')
const appVersion = require('electron').remote.app.getVersion()

// Devtron
// require('devtron').install()

// Close App
const closeApp = document.getElementById('close_app')
closeApp.addEventListener('click', () => 
    ipcRenderer.send('closing-app')
)

// received from ipcMain test
ipcRenderer.on('misc-sender', (event, args) => 
    appendTest = document.getElementById('testSender')
    appendTest.innerHTML += args
)

// Getting version
const appVersioning = document.getElementById('application')
appVersioning.innerHTML = appVersion

// Open site
const homeURL = document.getElementById('site')
homeURL.addEventListener('click', (e) => 
    e.preventDefault
    shell.openExternal("https://www.google.com/")
)

【问题讨论】:

这里有很多编辑和更正的语法错误,很难知道发生了什么。发布电子小提琴(github.com/electron/fiddle)的链接,然后我可以提供更多帮助。 AFAICT,您所做的基本上是正确的,您可能只是在将该消息发送到 webContents 和 webContents 及时收听它时遇到了一些竞争条件。 @ccnokes 进行了另一次编辑以包含来自 Electron Fiddle 的所有内容。 @Grim 我做了一些小改动来修复语法错误,一切正常。所以你做的是对的,只是被小细节绊倒了。这是工作小提琴 URL:gist.github.com/ccnokes/5150ebd95d4368048513e73f5c7d57be 【参考方案1】:

经过多次搜索和尝试,我想我终于知道如何将我的应用程序版本从 package.json 发送到 main 然后再发送到渲染器。我的问题出在我的app.on 中,我错过了dom-ready,这在阅读IPC Communication not working between Electron and window 后有所帮助:

main.js

const appVersion = process.env.npm_package_version

app.on('ready', () => 
  mainWindow.createWindow()
  Menu.setApplicationMenu(mainMenu)

  // Send version to renderer
  mainWindow.win.webContents.on('dom-ready', () => 
    console.log(`Trying to send app version to renderer: $appVersion`)
    mainWindow.win.webContents.send('app-version', appVersion)
  )
)

renderer.js

ipcRenderer.on('app-version', (event, args) => 
    const appVersion = document.getElementById('app_version')
    console.log(`Node version is $args`)
    appVersion.innerHTML += args
)

index.html

<div id="app_version"></div>

可能有更好的方法来做到这一点,但经过进一步研究,我读到:

Electron - How to know when renderer window is ready dom-ready 来自instance-events

这可行,但下一步是查看拉动process.env 是否是一种良好的安全做法。如果存在,我确实希望看到一些其他可能更好的方法的答案。

【讨论】:

dom-ready 上没有问题。挂钩dom-ready 只是为了向渲染器发出事件是矫枉过正(IMO)。看看我的回答【参考方案2】:

在回答您的问题之前,请查看您发送给渲染器的消息(如果您想要将应用程序版本发送到渲染进程),可以使用app.getVersion 完成。在您的主流程中,您必须使用app.setVersion("1.0") 设置应用程序版本,然后在您的渲染流程中您必须这样做

const  remote:  app   = require("electron");
app.getVersion();

对于您的实际问题 mainWindow.createWindow() 需要返回 BrowserWindow 的实例(查看您的代码,您没有返回它,而且您也没有阅读您在 mainWindow.js 上设置的 win 属性对象)。如果您想在问题中坚持使用当前代码,则必须这样做

mainWindow.win.webContents.send(...)

或者你应该这样做

mainWindow.js

// Modules
const  BrowserWindow  = require('electron')

// export mainWindow
exports.createWindow = () => 

    // BrowserWindow options
    // https://electronjs.org/docs/api/browser-window#new-browserwindowoptions
    const win = new BrowserWindow(
        minWidth: 400,
        minHeight: 400,
        frame: false,
        webPreferences: 
            nodeIntegration: true,
            backgroundThrottling: false
        
    )

    // Devtools
    win.webContents.openDevTools()

    // Load main window content
    win.loadURL(`file://$__dirname/index.html`)

    // Handle window closed
    win.on('closed', () => 
        this.win = null
    )
    return win;

main.js

const  app, ipcMain, Menu   = require('electron')
const appVersion = process.env.npm_package_version
const mainWindow = require('./renderer/mainWindow')

app.on('ready', () => 
    const win = mainWindow.createWindow(),
    console.log(`Trying to send app version to renderer: $appVersion`),
    win.webContents.send('app-version', appVersion),  
    Menu.setApplicationMenu(mainMenu)
)

【讨论】:

编辑了我的问题以解决答案。目前这不起作用。 @Gʀɪᴍ 在 const 声明中缺少初始化程序的原因是因为您没有从 mainWindow.createWindow() 返回任何值 复制mainWindow.jsmain.js 您编辑的问题在那里有答案。 win is not defined(这意味着您没有将 win 设置为 BrowserWindow 的实例) 我复制了你的内容,将this.win = new BrowserWindow 更改为const win = new BrowserWindow 并添加了return

以上是关于为啥我的 ipcMain 没有发送到 Electron 中的 ipcRenderer?的主要内容,如果未能解决你的问题,请参考以下文章

07.electron-(渲染进程和主进程)通信

为啥引用的模块没有解析?

为啥我的 php 没有将我的联系表单输入发送到我的电子邮件?

为啥我的失败事件没有发送到 AWS Dead Letter Queue DLQ?

为啥我的后端中的 axios 发布请求没有将任何数据发送回我的外部 api? -

为啥内核没有收到我的通用网络链接消息?