电子中2个浏览器窗口之间的通信

Posted

技术标签:

【中文标题】电子中2个浏览器窗口之间的通信【英文标题】:communication between 2 browser windows in electron 【发布时间】:2017-03-08 04:16:44 【问题描述】:

我需要构建一个跨越多个监视器屏幕的应用程序,如下所示: Electron 支持多个窗口,但如何在它们之间进行通信?

【问题讨论】:

【参考方案1】:

要记住的主要事情是,在 Electron 中,进程间通信是由 ipcMain(在主进程中)和 ipcRenderer(在所有创建的窗口中)完成的。如下所示: 从我在 GitHub cmets 中看到的 - 渲染器实例之间的直接通信是不允许的。一切都必须通过 mainProcess。

代码: ma​​inProcess.js:

function createWindow1 () 
  window1 = new BrowserWindow(width: 800,height: 600)
  window1.loadURL(`file://$__dirname/window1.html`)
  window1.webContents.openDevTools()
  window1.on('closed', function () 
     window1 = null
  )
  return window1

function createWindow2 () 
  window2 = new BrowserWindow(width: 1000, height: 600)
  window2.loadURL(`file://$__dirname/window2.html`)
  window2.webContents.openDevTools()
  window2.on('closed', function () 
    window2 = null
  )
  return window2


app.on('ready', () => 
  window1 = createWindow1();
  window2 = createWindow2();

  ipcMain.on('nameMsg', (event, arg) => 
  console.log("name inside main process is: ", arg); // this comes form within window 1 -> and into the mainProcess
  event.sender.send('nameReply',  not_right: false ) // sends back/replies to window 1 - "event" is a reference to this chanel.
  window2.webContents.send( 'forWin2', arg ); // sends the stuff from Window1 to Window2.
);

window1.html:

<body>
    <input type="text" id="name" value="" placeholder="Enter your name">
    <button type="button" id="sendName" >Send the name! </button>
</body>
<script>
   // You can also require other files to run in this process
   require('./window1.js')
</script>

window1.js:

const ipcRenderer = require('electron').ipcRenderer

let name = document.getElementById('name');

ButtonSendName = document.getElementById('sendName');
ButtonSendName.addEventListener('click', (event) => 
  ipcRenderer.send('nameMsg', name.value);
)

ipcRenderer.on('nameReply', (event, arg) => 
  console.log(arg) // why/what is not right..
);

window2.html:

<body>
  <p id = "showName"></p>
</body>

<script>
  require('./window2.js')
</script>

window2.js:

const  ipcRenderer  = require('electron')

showName = document.getElementById('showName')
ipcRenderer.on('forWin2', function (event, arg)
  console.log(arg);
  showName.innerHTML = arg;
);
console.log("I'm Window2");

演示会更好,但我不知道如何构建电子 CodeBin 应用程序。这张图给你一个想法:

享受电子的力量!

【讨论】:

很好的解释!谢谢。我不知道为什么,但每次我阅读有关 ipc 的文章时,我总是有一种强烈的感觉,即 Electron 开发人员过度设计了一些简单的东西,比如在两个窗口之间传递一个变量 :) @SystemsRebooter 我不是 Electron 开发人员,但由于每个 BrowserWindow 对象都是一个单独的进程,不幸的是它并不像您想象的那么简单。事实上,我们需要更多的工程才能让它更容易:)【参考方案2】:

编辑:我为此创建了一个存储库:electron-multi-monitor:

我们的项目也有类似的问题。然而,两个 BrowserWindows 都必须来回传递 JS 对象和函数。

通过 IPC 调用提出的解决方案是我们尝试的第一件事,但还不够。当您只需要传递一些小对象但您很快就会达到它的极限时,它非常有用,因为 Electron 将序列化通过 IPC 调用传递的所有数据。

我们前进的方式是使用window.opener 功能。我们使用电子生成一个main BrowserWindow,然后通过window.open() 打开想要的side Browser 窗口。 Electron 可以在它们生成时定位这些窗口。 接下来每个side 窗口将其HTML DOM 注册为main 窗口的JS 窗口实例。这样main 窗口就会引用side 窗口的DOM 和JS 窗口实例。 从这里开始,main 窗口可以完全控制所有可用的窗口,并且可以在所有窗口上渲染新的 HTML、传递 JS 对象、调用 JS 函数……。我们个人使用React Portals 来处理不同窗口上的渲染。

目前我不能分享一个完整的例子,但如果我有时间我会创建一个 github repo。

一些已经可以帮助你前进的事情:

BrowserWindows 应该有相同的affinity(参见BrowserWindow docs) 启用您的 webPreferences 的 nativeWindowOpen

仅供参考:您也可以直接在浏览器中使用此技术,但它们仍然不允许您在窗口中移动

【讨论】:

看起来亲和力已被弃用 是的@user1689987。似乎他们正在摆脱对子窗口使用不同进程的事实,这很好,因为它使设置亲和力变得不必要。 (有关已弃用的亲和力的更多信息:github.com/electron/electron/issues/18397)【参考方案3】:

如果你使用 window.open() 从主窗口的 renderer 进程打开弹出窗口,实际上可以通过 JS 在 2 个 Electron 窗口之间进行通信。这避免了通过 IPC 调用进行通信的需要。 See Docs.

例如:

//renderer process
let popupWindow = window.open(
  './popup.html', 
  'popup'       
  'width=800,height=600'
);

popupWindow.onload = () =>        
  //now we have access to popup window dom   
  popupWindow.document.body.appendChild(myDomElement);
;

请注意,要使其正常工作,您需要在最初创建主窗口时设置 nativeWindowOpen webPreferences 选项。

// main process
const mainWindow = new BrowserWindow(
  width: 800,
  height: 600,
  webPreferences: 
    nativeWindowOpen: true
  
)

【讨论】:

【参考方案4】:

根据您的要求...可以创建 SharedWorker 作为在窗口之间传输 MessagePorts 的代理。注意:SharedWorker 要求所有窗口从同一来源运行(可能不符合您的要求)。例如,在主窗口中创建一个 MessageChannel(它提供了两个端口),然后通过 SharedWorker 将 port1 传输到一个窗口,将 port2 传输到另一个窗口。现在两个窗口可以使用 postMessage 通过端口直接通信。作为奖励 postMessage 还支持可转让。我在玩这个想法,但还没有完全开发这个库,但你可以从这里正在进行的一些工作中得到这个想法:https://github.com/lneir/electron-direct-comm

【讨论】:

【参考方案5】:

每当我们谈论在 Electron 应用程序中从一个窗口到另一个窗口的通信时,您总是想考虑 IPC 系统,即进程间通信。

因此,您将在一个窗口中侦听一个事件,例如,表单提交。

提交表单后,您可以从该输入中取出文本并向 Electron 应用程序发出事件。

然后,Electron 应用程序将触发自己的事件并将事件发送到mainWindowmainWindow 将接收文本并将其附加到其列表中。

您可以像这样在辅助 window.html 文件中只使用原生 javascript 来启动它:

  document.querySelector('form').addEventListener('submit', event => 
    event.preventDefault();
  );

因此,以上假设您正在使用您尝试提交的表单。

【讨论】:

以上是关于电子中2个浏览器窗口之间的通信的主要内容,如果未能解决你的问题,请参考以下文章

2个浏览器窗口间通信

您如何在两个浏览器选项卡/窗口之间进行通信? [复制]

您如何在两个浏览器选项卡/窗口之间进行通信? [复制]

浏览器不同页面之间的通信

两个浏览器窗口间通信总结

http通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤