值得了解的「前端跨窗口通信」常用方法

前言

在某些场景下,为了用户打开的网页窗口之间能够互相感知,我们需要使用各种方式让同源、非同源的 窗口之间能够实时、高效的共享数据、互相通信

  • 打开某平台的多个页面,登录其中一个后,通知其他页面自动更新登录状态
  • 接入第三方登录,在新窗口登录操作成功后,通知页面用户的认证数据
  • 前篇文章的跨窗口动画 ……

值得了解的「前端跨窗口通信」常用方法

随着前端的发展和浏览器的更新,在不借助后端(轮询、websocket、sse)的情况下,前端自身也有较完善的方案实现高效、便捷的通信。

本文,我们一起学习纯前端跨窗口通信的几种常见方式:localStoragepostMessageBroadcast ChannelsharedWorker ,了解它们的相关知识与应用。

如果有收获还望 点赞+收藏 🌹

简介

API 同源限制 优点 缺点 原理 兼容性
localStorage 受限制 便捷、兼容性高 读写转换可能有问题 本地存储 &StorageEvent 主流支持
postMessage 不受限制 安全的跨源通信 只能点对点的传输数据 窗口引用 &MessageEvent 主流支持
BroadcastChannel 受限制 支持多对多的广播 单点推送需要手动处理 发布-订阅模式 主流支持
SharedWorker 受限制 性能强、不消耗主进程 使用较复杂、兼容性差 web worker线程 支持较差

兼容

出于兼容性考虑,我们可以在代码中判断浏览器是否支持当前方案 并采用降级方案

let broadcastChannel; 
if (window.BroadcastChannel) { 
    broadcastChannel = new BroadcastChannel('test'); 
}

if (broadcastChannel) {
    // ...
} else {
    window.onstorage = (e) => {  
    // ... 
    }
}

共享

保持页面之间数据同步的方式各有不同:

  • localStorage 使用的同一个storage对象,数据自带同步
  • SharedWorker 可以把数据存储到共享worker中,在通知时发送
  • BroadcastChannel & postMessage 只能保存在各自的窗口中,每份数据都是独立的

通信

它们的通信方式都继承自Event事件:

  • localStorage 页面中storage对象被修改时会触发 StorageEvent 事件
  • postMessageBroadcast ChannelsharedWorker的通信借助了MessageEvent事件

值得了解的「前端跨窗口通信」常用方法

API

值得了解的「前端跨窗口通信」常用方法

– localStorage

localStorage 允许你访问一个 Storage 对象,用于访问、修改特定域名下的本地存储数据。

localStorage.getItem("data-time");  
localStorage.setItem("data-time", Date.now());  
localStorage.clear("data-time");  

存储数据 受同源策略限制,即只有相同域名、协议和端口的页面才能相互访问各自的数据

当存储区域(localStorage)发生变化时,将在文档的 Window 对象上触发 storage 事件。

StorageEvent

// pageA
window.addEventListener('storage', (event) => {
    console.log(event);
    // do something...
});

storage 事件的属性主要有:

  1. key,代表被修改的键名
  2. url,代表发生改变的对象所在文档的 URL 地址
  3. oldValuenewValue,代表变化前后的值
  4. storageArea,代表被操作的storage对象(包含其他key的值)

值得了解的「前端跨窗口通信」常用方法

利用 localStorage 的本地存储——共享数据,通过监听 storage 事件触发回调——通知更新。

// pageB
localStorage.setItem("data-time", Date.now());  
localStorage.clear("data-time");  

// pageA
// *Console:StorageEvent {...}*
// *Console:StorageEvent {...}*

值得了解的「前端跨窗口通信」常用方法


– postMessage

window.postMessage 提供了一种不同窗口之间 安全通信 的方法,它允许来自一个窗口的文档发送消息到另一个窗口的文档,即使它们不是同源的

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow 是对其他窗口的引用:

  1. 例如 iframecontentWindow 属性
  2. 返回的窗口对象const opener = window.open(xxURL);
  3. 嵌套的窗口对象window.parent或者是window.frames

postMessage 方法接受两个参数:

  1. message,要发送的消息,它将被序列化
  2. targetOrigin,字符串”*”(表示无限制)或者一个 URI,用于指定哪些窗口能接收到消息事件

postMessage 方法会分发一个 MessageEvent 事件,接收消息的窗口可以根据需要自由处理此事件。

// otherWindow
window.addEventListener("message", (event) => { 
    console.log(event);
    // do something... 
});

MessageEvent

message 事件的属性有:

  1. data,从其他 window 中传递过来的消息对象
  2. origin,调用 postMessage 时消息发送方窗口的 origin,例如 https://example.orghttp://example.com:8080
  3. source,对发送消息的窗口对象的引用; 你可以使用此来在具有不同 origin 的两个窗口之间建立双向通信

值得了解的「前端跨窗口通信」常用方法

如果你想尽可能的避免安全问题,请始终提供一个有确切值的targetOrigin,并且在接收信息时使用 originsource 属性来检查消息的发送者的身份

– Broadcast Channel

值得了解的「前端跨窗口通信」常用方法

BroadcastChannel API 提供了简单的方法来 创建、连接频道,它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间通过频道进行 消息广播接收

// 创建、连接频道
const broadcastChannel = new BroadcastChannel("cross-window-broadcast");
// 广播信息
broadcastChannel.postMessage(data);
// 关闭频道
broadcastChannel.close();

BroadcastChannel() 构造函数接受一个参数,创建一个BroadcastChannel对象:

  1. channelName,表示频道名称的字符串

BroadcastChannel 对象与指定频道相连,提供两个方法:

  1. postMessage,向所有监听了相同频道的 BroadcastChannel 对象发送一条消息,消息内容可以是任意类型的数据
  2. close,关闭频道对象,不再接收新的消息
// 处理信息
broadcastChannel.onmessage = (event) => handleChannelMessage(event);
broadcastChannel.addEventListener('message', (event) => handleChannelMessage(event));

当频道收到一条消息时,会在关联的 BroadcastChannel 对象上触发 MessageEvent 事件。

message 事件的属性有:

  1. data,由消息发送者发送的数据
  2. origin,表示消息发送者来源的字符串
  3. source,消息事件源,可以是一个用于表示消息发送者的 WindowProxy (en-US)MessagePort 或 ServiceWorker 对象
  4. ports,与发送消息(通过频道发送消息或向 SharedWorker 发送消息)的频道相关联的 MessagePort 对象的数组。

值得了解的「前端跨窗口通信」常用方法


值得了解的「前端跨窗口通信」常用方法

– sharedWorker

值得了解的「前端跨窗口通信」常用方法

SharedWorker 是 HTML5 新增的 Web Workers 类型之一, 它允许多个同源的浏览上下文(比如 窗口、iframe、不同的Tab页)共享一个 worker 实例

SharedWorker 可以用于在多个页面之间 共享数据执行复杂计算,是一个强大的多线程工具。

其中 message 事件的属性与 BroadcastChannel 相同就不介绍了。

// html -> script
// 创建一个共享进程对象
const myWorker = new SharedWorker("./sharedWorked.js");
// 手动启动端口
myWorker.port.start();
// 监听信息的发送
myWorker.port.onmessage = (event) => {
    console.log('event:', event);
    handleWorkerMessage(event.data);
}

function sendWorkerMessage(data) {
    myWorker.port.postMessage(data);
}

function handleWorkerMessage(data) {
    console.log(data);
    // do something...
};
// sharedWorker.js
onconnect = function (event) {
  const port = event.ports[0];

  port.addEventListener("message", (event) {
    const result = "Result: " + event.data[0] * event.data[1];
    port.postMessage({result});
  });

  port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter.
};

值得了解的「前端跨窗口通信」常用方法

总结

本文介绍了几种常见的前端跨窗口通信方法和它们的优缺点,并进行了基础实践。

它们的兼容性、性能、便捷性和通信方式等各方面各有不同,开发时根据场景需要选择更合适的方案。

结语 🎉

不要光看不实践哦,希望能对你有所帮助~

持续更新前端知识,脚踏实地不水文,真的不关注一下吗~

才疏学浅,如有问题或建议还望指教!

原文链接:https://juejin.cn/post/7354589998313259044 作者:西维

(0)
上一篇 2024年4月7日 上午10:05
下一篇 2024年4月7日 上午10:16

相关推荐

发表回复

登录后才能评论