在electron
中创建桌面端应用时,根据功能需求会创建弹窗,并且可以在隐藏主界面后子弹窗依旧存在。遇到这类情况,可以重新使用electron
创建子窗口。
在 Electron 应用程序中,子弹窗(或称为“弹出窗口”)通常是指在主窗口之上打开的额外小窗口。这些窗口可以用于显示辅助信息、对话框、工具面板等。 子窗口一般用于显示辅助内容或执行任务,子窗口也是通过
new BrowserWindow
进行创建。
下面对子窗口的创建和父子窗口的通信进行介绍,以项目为例子进行说明:
时间沙漏 -- 番茄钟
我正在做一个时间管理工具准备来对electron
进行练手,页面仿照时光序,里面有一项是番茄钟功能
此界面在创建好番茄钟后,开始启动的番茄钟界面准备出现倒计时弹窗,并且弹窗需要可拖动。 如果是在主窗口中创建,弹窗就不能拖动出主窗口,所以需要创建一个子窗口来承载该倒计时弹窗。
【主窗口】
【倒计时窗口】
创建子弹窗
在主窗口点击开始番茄钟后,通过window.electronAPI.sqliteTomato
传递相应信息到后台进程中。
为了使用方便,我在preload.js
封装下ipcRenderer各类方法:
// 在渲染进行中不直接使用ipcRenderer
contextBridge.exposeInMainWorld("electronAPI", {
removeAllListeners: (name) => ipcRenderer.removeAllListeners(name),
ipcRendererSend: (ipcname, content) => ipcRenderer.send(ipcname, content),
ipcRendererOn: (ipcname, callback) => ipcRenderer.on(ipcname, callback),
sqliteTomato: (args) => ipcRenderer.invoke('sqlite-tomato', args),
sqliteTomatoNumber: (nums, callback) => ipcRenderer.on(`sqlite-tomato-${ nums }`, callback),
sqliteTomatoOnceNumber: (nums, callback) => ipcRenderer.once(`sqlite-tomato-${ nums }`, callback),
});
创建需要挂载到子弹窗上的页面countTomato.vue
,在vue-router
的index.js中添加该路径
然后定义好创建子窗口的方法,并且根据开发环境判断loadURL
加载的页面地址
- 参数分别为路径,窗口大小,传入信息
url
:vue-router
的路由名称,在下方loadURL
加载时使用,传入/countTomato
size
:窗口大小,里面存在w
和h
arg
:本来是要放一些从主窗口传进的信息的,目前暂时没有使用
- 如果childWindow存在,就不再创建新弹窗
- 根据我自己的需求,我隐藏了顶部栏
frame
,并设置不可改变大小resizeable
,背景透明transparent
const isDevelopment = !app.isPackaged, childWindow = null;
// 创建子弹窗函数
const createChildWindow = (url, size, arg)=>{
if (childWindow) { return ; }
let { w, h } = size;
// 实例化BrowserWindow对象
childWindow = new BrowserWindow({
width: w,
height: h,
frame: false, // 顶部栏隐藏
resizable: true, // 不能改变大小
transparent: true, // 背景透明
icon: path.join(__dirname, '../image/package.png'),
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
// 书写渲染进程中的配置
nodeIntegration: true,
contextIsolation: true,
}
})
// 判断是否在开发环境调试
let winUrl = isDevelopment ? 'http://localhost:5522' : `file://${path.resolve(__dirname, '../../')}/dist/index.html`;
childWindow.loadURL(`${winUrl}#${url}`);
}
如果弹窗出现,但是页面空调,可以看
winUrl
路径是否正确
子弹窗拖拽
因为没有使用默认的顶部栏,当前弹窗并不能进行拖拽,可以在页面上自定义一条顶部栏
CSS
样式中进行设置-webkit-app-region: drag;
,因为electron
使用chrome
内核,所以拖拽直接生效
子窗口关闭
自定义的顶部栏上添加关闭和最小化按钮,在进程文件中添加对应的监听方法
当前界面效果已完成,可以生成窗口,并将页面挂在子窗口中,子窗口可以拖拽到主窗口外面。
多弹窗通信
在番茄钟功能里,两个窗口之间需要互相通信,主窗口将开启的番茄钟信息传给子窗口,子窗口完成一次番茄钟后,将增加的次数传回主窗口。
在后台进行中设置监听和发送方法,根据窗口不同来进行监听
// 监听父子窗口通信
const connectWin = () => {
// 接受父窗口发送的消息
ipcMain.on('connect-father-window', async (event, arg) => {
childWindow?.webContents.send('send-child', arg);
})
// 接受子窗口发送的消息
ipcMain.on('connect-child-window', async (event, arg) => {
mainWindow?.webContents.send('send-father', arg);
})
}
页面上也添加相应的监听发送方法,以下是主窗口上的方法,子窗口的方法也是类似
// 发送给子窗口数据
window.electronAPI.ipcRendererSend('connect-father-window', JSON.stringify({ msg, data }));
// 接收子窗口数据
window.electronAPI.ipcRendererOn('send-father', (e, data) => {
let infos = JSON.parse(data);
switch (infos.msg) {
case 'window-create-finish': ...; break;
case 'tomato-finish': ...; break;
}
})
PS: 这里需要注意,在创建子窗口时,主窗口发送信息过来,子窗口是接收不到的。所以需要在子窗口渲染结束后,必须由子窗口先发送数据告知主窗口,然后主窗口再发送信息。
初始化子窗口创建和后续通信流程如下:
总体而言,这一部分还是比较简答,如果将思路打通,把方法封装下,做起来速度还是比较快的。