浏览器跨tab通信的几种办法
跨tab通信需求常见于一些有大量内容需要浏览或操作(比如博客或者CMS系统),同时需要保持数据一致性的场景。这些场景大部分可以引入后端服务,通过轮询等方式保持多个tab间的数据一致。本文介绍的是在不引入后端服务的情况下,通过纯前端的方案,来进行跨tab通信
什么场景下需要跨tab通信?
有一些日常中很常见的场景,如果支持跨tab通信,可能能给用户更好的体验。
博客Dark Mode切换
想象你在浏览 Dan Abramov的博客,你同时打开了好几个tab学习一系列知识。夜晚来临,你将一个页面切换成了Dark Mode。当你阅读完这一篇去阅读下一篇时,页面却不是以Dark Mode呈现的。你不得不按下F5刷新页面来寻求一致的阅读体验。
需要频繁登录的CMS系统
想象你在网店管理系统中管理你网店中的商品,你打开了好几个tab,分别管理商品类型,商品详情以及商品库存。你正在管理你的商品详情,突然你的登录session过期,你点击重新登录。但是当你切换到商品库存页面时,你发现你依旧无法操作,需要再次登录。你不得已点击了登录按钮,和你辛苦编辑了10分钟但没有保存的商品库存说了拜拜。
以上场景其实无伤大雅,你的系统依旧运转,核心功能依旧正常。但是借助跨tab通信,你能给用户提供更好更顺滑的体验。
Cookie & IndexedDB
要实现数据的一致,很容易想到的就是统一的数据存储。在有后端加入的时候,后端其实就是前端的统一数据存储。在纯前端的环境下,比较常见的数据存储包括
sessionStorage 由于只存活于单个tab session中,所以不能用于跨tab通信。localStorage 有其他的应用方式,下面会深入分析。所以在这个章节我们主要关注cookie和IndexedDB。
实现思路
实现思路其实非常简单,就是通过轮询数据源,来确保每个tab的数据都是最新的。
|
|
需要注意的是,cookie的读写是同步的,在cookie比较大的时候可能会阻塞主线程造成页面卡顿。而IndexedDB的API都是异步的,所以不会有cookie同步读写的性能问题。
采用这种统一数据源的方式实现跨tab通信思路上比较简单,但是轮询的方式在数据变更不频繁的情况下效率是比较低下的(本质上是一种consumer pull的方式)。所以更理想的情况是,通过事件去驱动consumer(producer push的方式)做出对应的响应。
localStorage Event

localStorage本质上也是一种数据源,但是不同于上面提到的两种数据源,当localStorage存储内容发生变更时,会发出对应的事件,同源下的所有tab都能接收到该事件。
|
|
当然这种方式也有一些弊端,使用时需要注意
- 和cookie类似,localStorage的读写也是同步的,所以当storage数据量很大的时候,读写可能本身就会阻塞主线程造成页面卡顿。
- localStorage 的value 只支持string,在传递复杂数据时稍显麻烦。
- 触发 setItem 的页面不会收到 storage event (FYI: https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent)。
Broadcast Channel

Broadcast Channel 是一个Web API,能够实现同源下跨tab,windows,frames,iframes甚至是workers的通信,同时不像localStorage,发送的消息对象可以是任何Object类型,非常适合用于跨tab通信。
|
|
Broadcast Channel 的弊端在于其浏览器兼容性,直至本文发布,Safari仍不支持 Broadcast Channel (FYI https://caniuse.com/?search=broadcast)。
ServiceWorker
ServiceWorker 更常见的场景可能是缓存数据和加载数据,提升网页加载速度。但是ServiceWorker本身也是支持发送消息的,因此也可以用于跨tab通信。
|
|
使用 ServiceWorker 做跨tab通信的弊端在于,你需要学习和了解ServiceWorker,如果你的网站本来没有使用ServiceWorker,仅仅为了跨tab通信就去使用ServiceWorker,或许会有一点本末倒置。
window.postMessage
上面提到的几种方式,都只能在同源情况下进行跨tab通信,如果要不同源的tab也能通信,就只能借助 window.postMessage 了。
|
|
总结
- 使用cookie或者IndexedDB可以简单的实现跨tab通信,但是需要考虑轮询的性能问题
- localStorage Event可以跨tab传输,但是也需要考虑性能以及一些边界场景
- 如果没有浏览器兼容性要求,Broadcast Channel或许是最好的选择
- ServiceWorker也可进行跨tab通信,但是你需要提前评估引入ServiceWorker的成本
- 如果需要跨域进行tab间通信,只能选择 window.postMessage
参考资料
- 本文链接: https://hateonion.me/posts/21oct14/
- 版权声明: 本站所有文章均采用 CC BY-NC-SA 4.0 协议进行许可。如需转载,请注明出处。