浏览器中的跨页面通信之Broadcast Channel、SharedWorker
Broadcast Channel
听到Broadcast Channel这个概念,在React的源码中,还有一个类似的概念MessageChannel(为了解决requestIdleCallback兼容性问题)
好文推荐:https://blog.csdn.net/weixin_44273311/article/details/136342234
好文推荐:https://blog.csdn.net/super_ying123/article/details/136284917
MD文档:https://developer.mozilla.org/zh-CN/docs/Web/API/BroadcastChannel
Broadcast Channel 是什么
-
BroadcastChannel 允许在相同的源(通常页面来自相同的网站)在浏览器上下文(windows,tabs,frames或者iframes)之间进行简单的通信。
-
Broadcast Channel 与 window.postMessage区别
- BroadcastChannel,叫做“广播频道”,官方文档说,该API是用于同源不同页面之间完成通信的功能。
- BroadcastChannel只能用于同源的页面之间进行通信,而window.postMessage却可以用于任何的页面之间
- 基于BroadcastChannel的同源策略,它无法完成跨域的数据传输,跨域的情况,我们还是使用window.postMessage来处理
Broadcast Channel使用
用构造函数创建一个实例
const bc = new BroadcastChannel('test_channel');
test_channel 参数用来指定channel的名称,用以标识这个 channel,在其他页面,可以通过传入相同的 name 来使用同一个广播频道,连接到相同名称的BroadcastChannel,可以监听到这个channel分发的消息。用 MDN 上的话来解释就是:
There is one single channel with this name for all browsing contexts with the same origin.
该 name 值可以通过实例的 bc.name属性获得
监听消息
-
广播创建完成后,就可以在页面监听广播的消息
bc.onmessage = function(e) { console.log('receive:', e.data); }; // 对于错误也可以绑定监听: bc.onmessageerror = function(e) { console.warn('error:', e); };
-
除了为
.onmessage
赋值这种方式,也可以使用addEventListener
来添加'message'
监听
发送消息
-
广播实例也有一个对应的
postMessage
用于发送消息bc.postMessage('hello')
关闭广播监听
-
取消当前页面的广播监听的方式
- 取消或者修改相应的
'message'
事件监听 - 使用 Broadcast Channel 实例为我们提供的
close
方法:bc.close();
- 取消或者修改相应的
-
两者是有区别的
-
取消
'message'
监听只是让页面不对广播消息进行响应,Broadcast Channel 仍然存在 -
而调用
close
方法这会切断与 Broadcast Channel 的连接,浏览器才能够尝试回收该对象,因为此时浏览器才会知道用户已经不需要使用广播频道了 -
在关闭后调用
postMessage
会出现如下报错 -
如果之后又再需要广播,则可以重新创建一个相同 name 的 Broadcast Channel
-
兼容性
-
BroadcastChannel 是一个非常简单的 API ,内部包含了跨上下文同源通信的接口。它没有定义消息传输协议,故不同上下文中的不同文档需要自己实现。目前来看兼容性方面也基本没有问题
SharedWorker
- JavaScript是单线程的,单线程有好处也有坏处。为了弥补JS单线程的坏处,webWorker随之被提出,它可以为JS创造多线程环境。如果还不了解webWorker的可以去官网初步了解一下。
- sharedWorker就是webWorker中的一种,它可以由所有同源页面共享,利用这个特性,我们就可以使用它来进行多标签页之前的通信
SharedWorker特点
- 跨域不共享,即多个标签页不能跨域
- 使用port发送和接收消息
- 如果url相同,且是同一个js,那么只会创建一个sharedWorker,多个页面共享这个sharedWorker
其实它和我们的webSocket实现多页面通讯的原理很类似,都是发送数据和接收数据这样的步骤,shardWorker就好比我们的webSocket服务器
SharedWorker使用
- 好文推荐:https://blog.csdn.net/zxcvb0825/article/details/135057537
- MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker
worker.js
// worker.js
const set = new Set()
onconnect = event => {
const port = event.ports[0]
set.add(port)
// 接收信息
port.onmessage = e => {
// 广播信息
set.forEach(p => {
p.postMessage(e.data)
})
}
// 发送信息
port.postMessage("worker广播信息")
}
pageA.html
<script>
const worker = new SharedWorker('./worker.js')
worker.port.onmessage = e => {
console.info("pageA收到消息", e.data)
}
</script>
pageB.html
<script>
const worker = new SharedWorker('./worker.js')
let btnB = document.getElementById("btnB");
let num = 0;
btnB.addEventListener("click", () => {
worker.port.postMessage(`客户端B发送的消息:${num++}`)
})
</script>
-
上面的代码就是一个最简单的sharedWorker的应用,我们在pageA页面中初始化了sharedWorker,并且设置了接收消息的监听函
数,当sharedWorker初始化完成之后,pageA便会接收到一条消息,如下图
- 然后我们在pageB中同样初始化了sharedWorker的示例,点击按钮广播消息,此时pageA便可以收到消息
pageA输出结果
调试sharedWorker
- 我们如何查看当前是运行的哪个sharedWorker呢?可以在浏览时输入:chrome://inspect。
- 找到sharedWorker选项,就可以看到运行的sharedWorker,如下图
兼容性
https://caniuse.com/?search=SharedWorker
- 调试不太方便、兼容性不太好,具体使用根据具体情况选择