Skip to content

跨标签页通信

写了对于跨标签页的通信,能聊一下这个吗?

在我们的项目中,有这样子的需求,在其中一个标签页上做了操作,其他页面需要同步对应的状态。这其实就涉及到浏览器的跨标签页通信了。

(思考过程)其实一开始考虑的方式是通过WebSocket来实现,不过WebSocket需要务器端的支持,而我们需要的仅仅是一个纯前端的支持,所以直接pass掉了。当然就考虑到了LocalStorage,通过事件window.onstorage来监听,只要其他标签页修改了LocalStorage中的内容,window.onstorage就会被触发,而且事件返回的数据也非常全面,我甚至用LocalStorage已经实现了一版,不过这个实现还是有比较明显的短板,我们想要的是实时通信,但是LocalStorage实际最大的作用是共享数据,而且随着我们的操作变多,LocalStorage中存放的内容也越来越多。后面又发现有SharedWorker,不过没有过多的研究,因为看到了兼容性就直接否定了。

**(确定解决方案)**最后发现了有BroadCastChannel API,研究了一下就发现,这个API完全和浏览器标签通信的原理契合的(埋钩子),可以很简单方便的实现跨标签页通信,最后我们技术方案的选择就是BroadCastChannel

不过具体的细节在工程化环境中还是有一些注意的点,特别是在我们现在响应式数据的工程中,像Vue方式处理的响应式数据,不能够直接通过BroadCastChannel管道进行传递,这是由于浏览器页签之间传递克隆数据原因引起的(埋钩子,引起面试官的询问,也可以带出另外的话题点)。

如果退出页面,BroadcastChannel的监听也需要退出,所以,如果是单独封装的类或函数,需要单独处理退出关闭的,一般我们会做闭包处理(埋钩子,很有可能引起另外一个谈论的话题点)。再返回一个函数即可

还有就是如果页面上这样的需求比较多,那么管道的命名需要规范,毕竟在同一个域下面,BroadCastChannel管道的名字是唯一的,我们指定了相关的规范协议,来统一管道的命名

**(处理结果)**通过这样处理之后,不需要复杂的处理,不需要共享数据,也不需要经过后端的中转。我们就可以直接在前端实现跨标签页的通信,而且通信还是实时的。最重要的这个API的兼容性还行,只有IE浏览器不兼容,不过我们的项目这些特殊的需求本身也没有考虑老旧的浏览器,所以用起来都非常舒畅

  1. BroadcastChannel的原理

BroadCastChannel API,他的原理就是一个命名管道,我们的浏览器每个标签页本身就是一个独立的进程,基于管道的通信,本来就是进程之间的通信方式之一。所以这个玩意就是为跨标签页通信存在的。

既然原理是命名管道,所以每个 BroadcastChannel 对象都需要使用一个唯一的名称来标识通道。一个页面通过 postMessage 方法将消息发送到频道中,而其他页面则可以监听 message 事件来接收这些消息

BroadcastChannel通信为什么还和克隆有关系?

因为不同标签页之间是通过消息传递数据,传递的消息可以是任意的类型,当然也可以是对象类型,而这对象类型我们不可能直接赋值传递的。因为不同的进程中不可能存放同一个地址的数据。因此肯定是做克隆传递。而响应式数据,比如在Vue3中都是通过Proxy做了代理的,那么在做克隆的时候就会出现问题,不能对Proxy代理的对象进行克隆,知道这个原理之后,解决也很简单,消除Vue3数据的响应式即可,可以直接使用展开运算符,也可以使用vue给我们提供的方法toRaw。

克隆proxy存在的问题

  • 克隆后的对象失去了Proxy的拦截功能,无法继续保持响应式
  • Proxy对象无法被直接序列化
  1. 你刚才说浏览器每个标签页本身就是一个独立的进程,这是什么意思?

每个浏览器标签页通常被视为一个独立的进程,而不是一个线程。这种多进程架构被称之为多进程浏览器,谷歌浏览器就是采用这种方式。

这种架构的方式的主要目的是提高浏览器的稳定性、安全性和性能。

在多进程浏览器中,每个标签页都独立运行在独立的进程中,这样一旦一个标签页崩溃或遇到问题,不会影响其他标签页和浏览器本身的稳定性。而每个进程都有属于自己的内存。

苏ICP备2025160170号-1 | 前端进化之路 | Released under the MIT License.