窗口间通讯那些事
文本主要讲解了如何使用uTools创建一个窗口,并且讲解了插件如何与创建的这个窗口进行通讯
electron的支持
众所周知,uTools
是基于electron
构建的应用,所以,技术上还是通过electron的API
来实现。
首先,我们先前往electron的ipcRenderer文档, 可以看到,ipcRenderer提供了很多接口:
- on
- off
- once
- addListener
- removeListener
- removeAllListeners
- send
- sendTo
- invoke
- sendSync
- postMessage
- sendToHost
其中,重点的就是sendTo
接口与on
接口,sendTo可以将数据发送到指定的窗口
的指定通道
中,on可以接收当前窗口
的指定通道
的消息
javascript
ipcRenderer.sendTo(id, channel, message);
ipcRenderer.on(channel, message => {
console.log('接收到消息:', message);
});
uTools的支持
uTools提供了一个API:createBrowserWindow 用于创建一个窗口,用法为:
typescript
const ubWindow = utools.createBrowserWindow(
// 创建窗口的html文件,路径是相对于plugin.json的相对路径
'sub.html',
{
// 用户是否可以自定义宽高
useContentSize: true,
// 是否隐藏任务栏图标
skipTaskbar: false,
// 初始宽度
width: 500,
// 初始高度
height: 100,
// 窗口是否置顶
alwaysOnTop: false,
// 是否显示窗口的框架,如果fasle,就可以自己定义最小化、最大化和关闭按钮
frame: true,
// 窗口是否透明
transparent: false,
// 窗口的背景颜色
backgroundColor: '#000000',
// 是否有阴影
hasShadow: false,
webPreferences: {
// 这个窗口的preload.js脚本,也是相对于plugin.json的相对路径
preload: 'javascript/sub.js'
}
}, () => {
// 窗口创建成功回调
try {
// 显示窗口
ubWindow.show();
// 判断是否是dev环境
if (utools.isDev()) {
// 如果是dev环境,打开开发者工具
ubWindow.webContents.openDevTools();
}
// 隐藏主窗口
} catch (e) {
console.error("打开子窗口失败", e);
}
});
实战
有了上面的基础,我们就很容易实现窗口通讯了
第一步
创建preload.js
文件,并在plugin.json
中指定preload.js
文件
json
{
"preload": "preload.js"
}
第二步
在preload.js
中,编写发送消息和接收消息的方法
javascript
const {ipcRenderer} = require('electron');
/**
* 发送消息到子窗口
* @param id {string} 窗口ID
* @param channel {string} 管道
* @param msg {string} 消息
*/
function sendMsgTo(id, channel, msg) {
ipcRenderer.sendTo(id, channel, msg)
}
/**
* 接收子窗口发过来的消息
* @param callback {(msg: string) => void} 接收消息回调
*/
function receiveMsg(channel, callback) {
ipcRenderer.on(channel, (_event, res) => {
callback(res);
})
}
window.preload = {
sendMsgTo, receiveMsg
}
第三步
创建子窗口sub.html
和sub.js
javascript
const { ipcRenderer } = require('electron')
// 插件的窗口ID
let parentId = null;
// 管道名称,可以自定义,只需要和发送时的管道名称一致即可
const channel = 'channel'
window.preload = {
/*** 接收主窗口发送过来的消息 ***/
receiveMsg: (callback) => {
ipcRenderer.on(channel, (event, res) => {
// 保存插件的窗口ID
parentId = event.senderId;
if (res) {
callback(res);
}
})
},
/*** 向插件主窗口发送消息 ***/
sendMsg: (msg) => {
if (parentId) {
ipcRenderer.sendTo(parentId, channel, msg);
}
}
}
html
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>子窗口</title>
</head>
<body>
</body>
<script>
window.preload.receiveMsg(msg => {
console.log('接收插件发送的消息:', msg);
// 发送消息到插件
window.preload.sendMsg('发送给插件的消息')
})
</script>
</html>
第四步
在插件中创建子窗口,并发送消息
typescript
// 与子窗口保持一致
const channel = 'channel';
const ubWindow = utools.createBrowserWindow(
// 创建窗口的html文件,路径是相对于plugin.json的相对路径
'sub.html',
{
// 用户是否可以自定义宽高
useContentSize: true,
// 是否隐藏任务栏图标
skipTaskbar: false,
// 初始宽度
width: 500,
// 初始高度
height: 100,
// 窗口是否置顶
alwaysOnTop: false,
// 是否显示窗口的框架,如果fasle,就可以自己定义最小化、最大化和关闭按钮
frame: true,
// 窗口是否透明
transparent: false,
// 窗口的背景颜色
backgroundColor: '#000000',
// 是否有阴影
hasShadow: false,
webPreferences: {
// 这个窗口的preload.js脚本,也是相对于plugin.json的相对路径
preload: 'javascript/sub.js'
}
}, () => {
// 窗口创建成功回调
try {
// 显示窗口
ubWindow.show();
window.preload.sendMsgTo(this.ubWindow.webContents.id, channel,
'插件发送消息给主窗口'
);
window.preload.receiveMsg(msg => {
console.log('接收到子窗口发送的消息', msg);
})
// 判断是否是dev环境
if (utools.isDev()) {
// 如果是dev环境,打开开发者工具
ubWindow.webContents.openDevTools();
}
// 隐藏主窗口
} catch (e) {
console.error("打开子窗口失败", e);
}
});
至此,窗口间通讯就完成了
注意
- 发送的消息可以是
任意对象
,例子中只是使用了字符串,并不是只能发送字符串 - 发送的对象需要
可序列化
,例如vue的ref包装的对象就是不可序列化的,发送会报错 管道可以使用多个
,不同的功能可以使用不同的管道,只需要发送和监听的管道一致即可,例子中只是发送了一个进行举例