记录---浏览器多窗口通信有效实践总结
🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
如何跨越不同窗口通信
在现代 Web 开发中,多个窗口或标签页之间的通信成为了越来越常见的需求,尤其是在应用需要同步数据、共享状态或进行实时更新的场景中。不同窗口间的通信方式有很多种,选择合适的方式可以大大提高开发效率和用户体验。本文将详细介绍几种常见的浏览器多窗口通信技术,并结合实际代码示例,分析它们的优缺点及兼容性。
1、window.postMessage的使用实践
window.postMessage 是一种用于跨域或同域窗口间安全通信的 API。通过 postMessage,我们可以轻松实现父子窗口、同域不同标签页或跨域之间的数据传递。
以下为实际运用中常用的2种用法:
1.1 iframe使用获得窗口引用
通过 window.postMessage
,父页面可以与嵌套的 iframe
进行通信。关键是要正确获得 iframe
的引用,然后通过该引用发送消息
父页面 parent.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <button onclick= "send()" >向iframe窗口发送消息< /button > <iframe id = "myIframe" src= "http://localhost:5500/child.html" >< /iframe > <script> function send() { const iframe = document.getElementById( 'myIframe' ); // 获得iframe窗口得window,向 iframe 发送消息 iframe.contentWindow.postMessage( 'Hello from Parent' , '*' ); } // 监听来自 iframe 的消息 window.addEventListener( 'message' , (event) => { if (event.origin === 'http://localhost:5500' && typeof event.data === "string" ) { console.log( '来自 iframe 的消息:' , event.data); } }); < /script > |
iframe 页面 child.html
1 2 3 4 5 6 7 8 9 10 11 | <button onclick= "send()" >向父窗口发送消息< /button > <script> function send() { // 向父页面发送消息 window.parent.postMessage( "Hello from Child" , "*" ); } // 监听来自父页面的消息 window.addEventListener( "message" , (event) => { console.log( "来自父页面的消息:" , event.data); }); < /script > |
注意:本地直接打开文件为file://
协议,不能使用,浏览器会将 file://
协议视为一个特殊的源,因为安全策略会无法使用
解决办法:将你的代码部署在一个本地开发服务器上(如使用 http-server
、Live Server
或其他简单的 HTTP 服务器)。这样,你就可以通过 http://localhost/
进行通信
1.2 window.open使用,获取窗口引用
通过 window.open
打开一个新的窗口或标签页时,我们可以获得新窗口的引用,并通过 postMessage
实现通信。
父页面 parent.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <button onclick= "openWin()" >打开新窗口< /button > <button onclick= "send()" >发送消息< /button > <script> let win = null function openWin() { win = window. open ( 'http://localhost:5500/child.html' , '_blank' ); } function send() { // 向新窗口发送消息 win && win.postMessage( 'Hello from Parent' , '*' ); } // 监听来自 child 窗口的消息 window.addEventListener( 'message' , (event) => { if (event.origin === 'http://localhost:5500' && typeof event.data === "string" ) { window.alert( '来自 child 窗口 的消息:' + event.data); } }); |
子页面child.html
1 2 3 4 5 6 7 8 9 10 11 12 13 | <button onclick= "send()" >向父窗口发送消息< /button > <script> function send() { // 向父页面发送消息 window.opener.postMessage( 'Hello from Child' , '*' ); } // 监听来自父页面的消息 window.addEventListener( "message" , (event) => { if (event.origin === 'http://localhost:5500' && typeof event.data === "string" ) { window.alert( "来自父页面的消息:" + event.data); } }); < /script > |
兼容性:
window.open
和postMessage
在大多数现代浏览器中得到支持。需要注意:如果打开的窗口被浏览器阻止弹出,通信无法进行。
2、客户端存储 + 定时器实时刷新监听【不推荐】
通过客户端存储(如 cookie
、localStorage
或 sessionStorage
)结合定时器(如 setInterval
或 requestAnimationFrame
),可以实现同源域名下不同窗口之间的通信。此方式利用定时器定时检查存储值的变化,但缺点是浪费性能资源,且数据更新不够实时
父页面 parent.html
1 2 | // 父页面向 storage 写入数据 localStorage.setItem( 'message' , 'Hello from Page' ); |
1 2 3 4 5 6 7 | // 子页面 定时检查 localStorage 变化 setInterval(() => { const message = localStorage.getItem( 'message' ); if (message) { console.log( 'Received message:' , message); } }, 1000); |
3、StorageEvent 事件监听
当一个页面修改了 localStorage
或 sessionStorage
,其他同源的页面可以通过监听 storage
事件来获取更新。此方法适合同一域下多个页面之间的通信。
父页面 parent.html
1 2 3 4 5 6 7 8 9 10 11 12 13 | <button onclick= "send()" >向子窗口发送消息< /button > <script> let i = 1 function send() { // 向新窗口发送消息 localStorage.setItem( 'child-message' , 'Hello from Parent' + i++); } window.addEventListener( 'storage' , (event) => { if (event.key === 'parent-message' ) { window.alert(event.newValue); } }); < /script > |
子页面child.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <button onclick= "send()" >向父窗口发送消息< /button > <script> let i = 1 function send() { // 向父页面发送消息 localStorage.setItem( 'parent-message' , 'Hello from Child' + i++); } // 监听来自父页面的消息 window.addEventListener( 'storage' , (event) => { if (event.key === 'child-message' ) { window.alert(event.newValue); } }); < /script > |
兼容性:
StorageEvent
事件在大多数现代浏览器中都得到了支持,包括 Chrome、Firefox、Safari 和 Edge。它仅在同源的多个页面之间有效,无法跨域使用。
4、Broadcast Channel
BroadcastChannel
API 允许同源的多个窗口、标签页、iframe 或 Web Worker 之间进行消息广播。这种方法非常适合同一应用中多个窗口或标签页之间的实时通信。
父页面 parent.html
1 2 3 4 5 6 7 8 9 10 11 12 | <button onclick= "send()" >向子窗口发送消息< /button > <script> function send() { // 发送消息到广播频道 const channel = new BroadcastChannel( 'parnt-message' ); channel.postMessage( 'Hello from Parent' ); } // 监听广播频道的消息 const channel = new BroadcastChannel( 'child-message' ); channel.addEventListener( 'message' , (event) => { window.alert(event.data) }); |
子页面child.html
1 2 3 4 5 6 7 8 9 10 11 12 | <button onclick= "send()" >向父窗口发送消息< /button > <script> function send() { // 发送消息到广播频道 const channel = new BroadcastChannel( 'child-message' ); channel.postMessage( 'Hello from Child' ); } // 监听广播频道的消息 const channel = new BroadcastChannel( 'parnt-message' ); channel.addEventListener( 'message' , (event) => { window.alert(event.data) }); |
兼容性:
BroadcastChannel
在 Chrome、Firefox 和 Edge 中得到支持,但 Safari 目前不支持,且不适用于 IE 或较旧的浏览器版本。
5、SharedWorker
SharedWorker
是一种非常强大的技术,允许多个窗口、标签页或 iframe 共享同一个后台线程。它适用于跨窗口的实时数据共享和复杂计算任务。
如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。
worker.js:执行指定 url 脚本的共享 web worker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 共享线程worker.js const ports = []; self.onconnect = function (event) { const port = event.ports[0]; ports.push(port); // 存储 port(端口) // 初始化发送消息 port.postMessage( 'Hello from SharedWorker!' ); // 监听消息 port.onmessage = function (e) { const index = ports.indexOf(port); // 获取当前的port的索引 // const currentPort = ports[index]; // 当前端口 // 向其他端口发送消息 ports.forEach((item, idx) => { if (index !== idx) { item.postMessage( '消息: ' + e.data); } }) }; }; |
self.onconnect 监听多个页面与 SharedWorker 的连接。每当一个新的页面连接到共享工作线程时,onconnect 事件会被触发。在 onconnect 事件处理函数中,我们可以通过 event.ports[0] 获取页面连接的 port,然后通过 port 进行消息传递
在共享线程中浏览器控制台无法显示任何打印信息:
SharedWorker
是一种共享工作线程,它用于处理多个浏览器窗口或标签页之间的共享计算任务,并且运行在独立的线程中。工作线程(包括SharedWorker
)不具备浏览器的 DOM 环境没有
window
对象,所以console
、alert
等方法都是无法使用的。
如果我们需要调试
SharedWorker
,可以使用调试Worker
的方法,在浏览器地址栏中输入chrome://inspect/#workers
,这样就可以看到当前页面中的SharedWorker
edge游览器:
edge://inspect/#workers
父页面 parent.html
1 2 3 4 5 6 7 8 9 10 11 12 13 | <button onclick= "send()" >向子窗口发送消息< /button > <script> // 创建 SharedWorker 实例 const worker = new SharedWorker( "worker.js" ); // 接收来自 worker 的消息 worker.port.onmessage = function (event) { window.alert(event.data); }; function send() { const message = "你好我是父窗口" ; worker.port.postMessage(message); } < /script > |
子页面child.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <button onclick= "send()" >向父窗口发送消息< /button > <script> // 创建 SharedWorker 实例 const worker = new SharedWorker( "worker.js" ); // 接收来自 worker 的消息 worker.port.onmessage = function (event) { window.alert(event.data); }; function send() { const message = "你好我是父窗口" ; worker.port.postMessage(message); } < /script > |
兼容性:
SharedWorker
不支持 Internet Explorer,Safari 也存在某些限制,尤其是在移动设备上兼容性较差,在一些老旧浏览器中无法使用
总结
浏览器多窗口通信有多种方法可供选择,每种方法都有其适用场景。以下是各方法的优缺点总结:
根据项目需求选择合适的通信方式,可以大大提升应用的性能和用户体验。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 本地部署 DeepSeek:小白也能轻松搞定!
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 如何基于DeepSeek开展AI项目