第五章-处理多窗口 | Electron实战

使用JavaScript Set数据结构跟踪多个窗口

促进主进程和多个渲染器进程之间的通信

使用Node APIs检查应用程序运行在那个平台上

现在,当Fire Sale启动时,它为UI创建一个窗口。当该窗口关闭时,应用程序退出。虽然这种行为完全可以接受,但我们通常希望能够打开多个独立的窗口。在本章中,我们将Fire Sale从一个单窗口应用程序转换为一个支持多个窗口的应用程序。在此过程中,我们将探索新的Electron APIs以及一些最近添加的JavaScript。我们还将探讨在将一个主进程配置为与一个渲染器进程通信,并对其进行重构以管理可变数量的渲染器进程时出现的问题的解决方案。本章末尾的完整代码可以在。 然而我们从第4章-使用本机文件对话框和帮助进程间通讯的分支开始。

图5.1 在第四章中,我们建立了主进程和一个渲染进程之间的通信。

第五章-处理多窗口 | Electron实战

图5.2 在本章中,我们将更新Fire Sale以支持多个窗口并促进他们之间的沟通。

第五章-处理多窗口 | Electron实战

我们首先实例化一个Set数据结构,该结构于2015年添加到JavaScript中,跟踪用户的所有窗口。接下来,我们创建一个函数来管理单个窗口的生命周期。在这之后,我们修改在第4章中创建的函数,以提示用户选择一个文件并打开它以指向正确的窗口。此外,我们还将处理一些常见的突发情况和沿途出现的其他问题,比如互相遮挡的窗口。

创建和管理多个窗口

Sets 是JavaScript的一个新的数据结构,是在ES2015规范中添加的。Set是唯一元素的集合;数组中可以有重复的值。我选择使用set而不是数组,因为这样更容易删除元素。这个清单显示了如何用JavaScript创建一个Set。

列表5.1 创建一个跟踪新窗口的集合: ./app/main.js

const windows = new Set();

对于数组,我们要么找到窗口的索引并删除它,要么创建一个没有该窗口的数组。这两种方法都不像调用Set上的delete方法并将引用传递给要删除的窗口那样简单。

有了跟踪应用程序所有窗口的数据结构,下一步是将创建BrowserWindow(列表5.2)从应用程序的"ready"事件监听器移到它自己的函数中。

const createWindow = exports.createWindow = () => { let newWindow = new BrowserWindow({ show: false, webPreferences: { // WebPreferences中的nodeIntegrationInWorker选项设置为true nodeIntegration: true } }); newWindow.loadFile('app/index.html'); newWindow.once('ready-to-show', () => { newWindow.show(); }); newWindow.on('closed', () => { windows.delete(newWindow); //从已关闭的窗口Set中移除引用 newWindow = null; }); windows.add(newWindow); //将窗口添加到已打开时设置的窗口 return newWindow; };

这个createWindow()函数创建一个BrowserWindow实例并将其添加到我们在清单5.1中创建的一组窗口中。接下来,我们重复前面几章中创建新窗口的步骤。关闭窗口将其从集合中移除,最后,我们返回对刚刚创建的窗口的引用,我们下一章需要这个参考资料。

当应用程序准备好,调用新的createWindow()函数,如下面的清单所示。应用程序应该以与实现此更改之前相同的方式启动,但它也为在其他上下文中创建额外的窗口奠定了基础。

列表5.3 在应用程序就绪时创建窗口: ./app/main.js

app.on('ready', () => { createWindow(); });

应用程序像以前一样启动,但是如果您尝试单击Open File按钮,您会注意到它已经坏了。这是因为我们仍然在一些地方引用mainWindow。它在dialog.showOpenDialog()中引用,以在macOS中将对话框显示为工作表。最重要的是,在从文件系统读取文件内容并将其发送到窗口之后,openFile()中引用了它。

主进程和多个窗口之间的通信

拥有多个窗口会引发一个问题:我们将文件路径和内容发送到那个窗口?为了支持多个窗口,这两个函数必须引用应该显示对话框的窗口和发送内容,如图5.3所示。

第五章-处理多窗口 | Electron实战

图5.3 要确定要将文件的内容发送到那个窗口,渲染器进程在与调用getFileFromUser()的主进程通信时必须发送对自身的引用。

在清单5.4中,让我们重构getFileFromUser()函数,以接受一个给定的窗口作为一个参数,而不是总是假设范围中有一个mainWindow实例。

列表5.4 重构getFileFromUser()以处理特定的窗口: ./app/main.js

const getFileFromUser = exports.getFileFromUser = (targetWindow) => { //获取对浏览器窗口的引用,以确定应该显示文件对话框的窗口,然后加载用户选择的文件。 const files = dialog.showOpenDialog(targetWindow, { //showopendialog()获取对浏览器窗口对象的引用。 properties: ['openFile'], filters: [ { name: 'Text Files', extensions: ['txt'] }, { name: 'Markdown Files', extensions: ['md', 'markdown'] } ] }); if (files) { openFile(targetWindow, files[0]); } // openFile()函数作用是:获取对浏览器窗口对象的引用,以确定那个窗口应该接受用户打开的文件的内容。 };

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssffp.html