浏览器标签页窗口通信
本文将为你介绍窗口通信使用场景、应用、实现
一、背景
产品: 前端,你这个创建页面需要新开标签页,然后创建成功,你这个本来的页面需要更新列表。
我: 前端做不了!
私下,稍加研究,看看怎么做,然后,下面给读者看看,我是如何做的。
二、窗口通信怎么做?
首先,非常感谢自己,因为在之前很喜欢阅读很多技术文章和视频,在之前就有看过一次分析两个窗口粒子效果。当时看了当中就提到浏览器是如何通信的。
因此,所以就结合自己的需求,似乎就刚刚满足,于是有了思路以后,就开始了解相关 API
,了解以后有三种实现方式,如下:
- BroadcastChannel
- SharedWorker
- localStorage/sessionStorage
这里,我就看 BroadcastChannel 这个 API
,所以接下来我会根据他来实现。
因此就可以了解到,当你需要和我一样实现相同的场景,就可以联想到这就是窗口通信。
三、利用 BroadcastChannel 实现标签页通信
BroadcastChannel
接口代理了一个命名频道,可以让指定 origin 下的任意 browsing context 来订阅它。它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信。通过触发一个 message
事件,消息可以广播到所有监听了该频道的 BroadcastChannel
对象。 查看MDN看更多
核心使用的方法:
BroadcastChannel.onmessage
事件处理器,用于定义当该对象上触发了message
事件时要执行的函数。BroadcastChannel.postMessage()
向所有监听了相同频道的BroadcastChannel
对象发送一条消息,消息内容可以是任意类型的数据。
我本人使用的是 Vue
项目开发的,考虑到各位技术栈不同这里,这里我会分别实现 Js
、Vue
、React
的三种方式
3.1 Js 实现
这里封装 createBroadcastChannel
javascript
const createBroadcastChannel = (channelName) => {
// 创建
const channel = new BroadcastChannel(channelName);
// 发送
const sendMessage = (message) => {
channel.postMessage(message);
};
// 处理接收
const receiveMessage = (callback) => {
channel.onmessage = (event) => {
callback(event.data);
};
};
// 关闭通信
const close = () => {
channel.close();
};
return {
channel,
sendMessage,
receiveMessage,
close,
};
};
export default createBroadcastChannel;
如何使用
在 createBroadcastChannel01.html
html
<body>
<button>发送</button>
<script src="./createBroadcastChannel.js"></script>
<script>
const { sendMessage } = createBroadcastChannel('test_channel');
document.querySelector('button').addEventListener('click', () => {
sendMessage('Hello World');
});
</script>
</body>
在 createBroadcastChannel02.html
html
<body>
<script src="./createBroadcastChannel.js"></script>
<script>
const { receiveMessage } = createBroadcastChannel('test_channel');
receiveMessage((event) => {
console.log('成功接收:' + event);
})
</script>
</body>
当我点击发送的时候,就会获得一下结果,如图
到这里,我们就已经知道当你在使用的时候,需要在设计到通信的页面创建相同的频道,保证通信一致,这样才能保证接收到正确的数据,即 createBroadcastChannel('test_channel')
这段代码。所以通信到这里,笔者跟着实现一遍就已经掌握。
3.2 Vue 实现
Vue
我会实现一个 Vue3 + Ts 的 hook ,代码如下:
ts
import { onBeforeUnmount, onMounted, ref, type Ref } from "vue";
export interface UseBroadcastChannelReturn<D, P> {
channel: Ref<BroadcastChannel | undefined>;
data: Ref<D>;
post: (data: P) => void;
close: () => void;
error: Ref<Event | null>;
}
export function useBroadcastChannel<D, P>(
channelName: string
): UseBroadcastChannelReturn<D, P> {
const channel = ref<BroadcastChannel>();
const data = ref<D>();
const error = ref<Event | null>(null);
onMounted(() => {
try {
channel.value = new BroadcastChannel(channelName);
channel.value.onmessage = event => {
data.value = event.data;
};
} catch (e) {
error.value = e;
}
});
onBeforeUnmount(() => {
close();
});
const post = (data: P) => {
if (channel.value) {
channel.value.postMessage(data);
}
};
const close = () => {
if (channel.value) {
channel.value.close();
}
};
return {
channel,
data,
post,
close,
error
};
}
3.3 React 实现
我会实现React
个 React + Ts 的 hook ,代码如下:
ts
import React, { useEffect, useRef, useState } from 'react';
export interface UseBroadcastChannelReturn<D, P> {
channel: BroadcastChannel | undefined;
data: D | undefined;
post: (data: P) => void;
close: () => void;
error: Event | null;
}
export function useBroadcastChannel<D, P>(
channelName: string
): UseBroadcastChannelReturn<D, P> {
const [channel, setChannel] = useState<BroadcastChannel | undefined>();
const [data, setData] = useState<D>();
const [error, setError] = useState<Event | null>(null);
const channelRef = useRef<BroadcastChannel>();
useEffect(() => {
try {
const newChannel = new BroadcastChannel(channelName);
newChannel.onmessage = (event) => {
setData(event.data);
};
channelRef.current = newChannel;
setChannel(newChannel);
} catch (e) {
setError(e);
}
return () => {
close();
};
}, [channelName]);
const post = (dataToSend: P) => {
if (channelRef.current) {
channelRef.current.postMessage(dataToSend);
}
};
const close = () => {
if (channelRef.current) {
channelRef.current.close();
}
};
return {
channel,
data,
post,
close,
error
};
}
不管是 Vue
还是 React
使用方式都参考 Js
的方式,返回了 channel
是方便使用更多的 API
四、总结
到这里就结束了,我们讨论了窗口通信是怎么做的。以及在什么场景下会使用到。BroadcastChannel
是一个非常强大的API,帮助我实现了标签页的通信。希望这篇文章能帮助到你,就像我曾经也被其他笔者帮助一样,我只是将这份热爱传递下去,感谢您阅读到最后,下期再见。