文章目录
- 引入
- 实现效果
- 思路
- 主进程模块
- 渲染进程模块
- 测试效果
引入
demo项目地址
窗口工具类系列文章:
封装窗口工具类【1】雏形
我们思考一下窗口间的关系,窗口创建和销毁的一些动作,例如父子窗口,窗口组合等等,还有一些窗口只能有一个,而某些窗口可以有很多个,按我们当前的窗口创建逻辑,是无法维护一个很复杂的窗口系统的,这就需要我们引入一个窗口组,里面维护各个窗口之间的关系和状态。
实现效果
- 没有传入key时,默认用窗口id作为窗口组中元素的key
- 窗口销毁时,元素正常从窗口组中移除
- 传入相同的key时,旧窗口被聚焦
思路
从如下场景出发:
- 我们希望一个唯一窗口被创建后,当再次执行窗口创建时,只是聚焦旧窗口,而不是重新创建窗口。
- 如何快速的获取某个已建窗口的 windowId或webContentsId,直接用于窗口间的通信
解决方案:
- 维护一个窗口组,即一个map集合,key作为窗口的唯一标识,每次创建时必须传入key,新建窗口通过判断key是否已存在来聚焦旧窗口
- map集合的值存储一个对象,对象内存储窗口的windowId和webContentsId,我们在创建窗口时就已知key,便可以很方便的用其获取webContentsId用于在渲染进程直接与另一个窗口的渲染进程进行通信。
主进程模块
1.补充窗口组元素的声明:
- electron\electron-env.d.ts
...
/** 一些仅在electron中使用的声明 */
declare global {
// 窗口组
interface WindowGroup {
webContentsId: number; // 窗口的渲染层id
windowId: number; // 窗口id
}
}
2.在窗口工具类中补充窗口组属性,并在构造方法中初始化
export class WindowUtils {
group: Map<string, WindowGroup>; // 窗口组 key就是传入的key值,如果没传,则取窗口的id作为key值
/**
* 构造方法,初始化属性
*/
constructor() {
this.group = new Map();
}
}
3.在窗口新建的方法中补充逻辑:
- 创建窗口前补充通过key判断已有窗口则聚焦旧窗口逻辑
- 窗口创建后,补充逻辑将窗口信息和key添加到窗口组中
- 并在最后补充窗口的通用事件绑定方法,里面补充窗口关闭时清理掉在窗口组中对应的元素
/**
* 创建窗口
* @param windowConfig 窗口创建参数
*/
createWindows(windowConfig: IWindowConfig): BrowserWindow {
// 先通过key判断是否已窗口,有则聚焦
let windowKey = windowConfig.key;
if (windowKey && windowKey.length > 0) {
/// 先从窗口组中取出记录
const wg: WindowGroup = this.group.get(windowKey);
if (wg) {
/// 根据记录中的窗口id获取窗口,假如存在该窗口,则聚焦该窗口
const oldWin = BrowserWindow.fromId(wg.windowId);
if (oldWin) {
oldWin.focus();
return oldWin;
}
}
}
// 创建窗口对象
...
// 将窗口的关键信息与key关联,存入窗口组中
windowKey = windowKey || win.id.toString();
this.group.set(windowKey, {
windowId: win.id,
webContentsId: win.webContents.id,
});
// 根据当前环境加载页面,并传递参数
...
// 绑定通用窗口事件
this.bindWindowEvent(win, windowConfig);
console.log(this.group);
return win;
}
/**
* 绑定窗口事件
* @param win 窗口对象
* @param windowConfig 窗口创建参数
*/
bindWindowEvent(win: BrowserWindow, windowConfig: IWindowConfig) {
// 窗口关闭监听,此事件触发时,窗口即将关闭,可以拒绝关闭,此时窗口对象还未销毁
win.on("close", () => {
/// 设置窗口透明
win.setOpacity(0);
/// 在窗口组中删除
const key = windowConfig.key || win.id.toString();
this.group.delete(key);
});
// 此事件触发时,窗口已关闭,窗口对象已销毁
win.on("closed", () => {
// 在窗口对象被关闭时,取消订阅所有与该窗口相关的事件
win.removeAllListeners();
// 引用置空
win = null;
});
}
3.应用启动后的第一个窗口我们把它当做主窗口,也补充个唯一key =>main,方便区分其他窗口
- electron\main\index.ts
win = windowUtils.createWindows({
route:"/",
key:'main'
});
渲染进程模块
我们渲染进程创建窗口时传入key
- src\components\demo\Index.vue
<template>
<input v-model="windowKey" placeholder="输入新建窗口的key"/>
</template>
<script setup lang="ts">
...
const windowKey = ref("");
...
electronUtils.createWindow({
route: windowPath.value,
key:windowKey.value,
param: JSON.stringify({
message: "向你问个好~~",
}),
});
</script>
测试效果
- 没有传入key时,默认用窗口id作为窗口组中元素的key
- 窗口销毁时,元素正常从窗口组中移除
- 传入相同的key时,旧窗口被聚焦