前端实现跨标签页通信

发布时间 2023-11-28 13:55:02作者: wanglei1900

标签页跨页通信

需求:浏览器不同 tab 标签页之间是独立的, 如果要通信必须通过特殊手段来实现跨标签页通信。

1.BroadcastChannel(webworker 可用)

BroadcastChannel 接口代理了一个命名频道,可以让指定 origin 下的任意 browsing context 来订阅它。它允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信。通过触发一个 message 事件,消息可以广播到所有监听了该频道的 BroadcastChannel 对象。

BroadcastChannel 顾名思义 广播频道,这个 WEB API 设计用来跨标签页之间通信

  • 属性

    • BroadcastChannel.name 频道名称,返回 DOMString。
  • 事件处理程序

    • BroadcastChannel.onmessage(),或 BroadcastChannel.addEventListener('message',callback) 事件处理器,用于定义当该对象上触发了 message 事件时要执行的函数。

    • BroadcastChannel.onmessageerror(),或 BroadcastChannel.addEventListener('messageerror', callback) 事件处理器,用于定义当该对象上触发了类型为 MessageError 的 MessageEvent 事件时要执行的函数。当接收到一条无法反序列化的消息时会触发此事件。

  • 方法

    • BroadcastChannel.postMessage() 向所有监听了相同频道的 BroadcastChannel 对象发送一条消息,消息内容可以是任意类型的数据。
    • BroadcastChannel.close() 关闭频道对象,告诉它不要再接收新的消息,并允许它最终被垃圾回收。
// 在一个标签页中发送消息
const channel = new BroadcastChannel("myChannel");
channel.postMessage("Hello,Bro!");
// 在另一个或其他所有标签页中接受同频道消息
const channel = new BroadcastChannel("myChannel");
channel.addEventListener("message", event => {
  console.log("Received", event.data); // Hello,Bro!
});
// 断开频道连接
channel.close();

2.StorageEvent 事件

当一个标签页 localStorage 变化时(sessionStorage 无效),同源下另一个或其他所有标签页使用 DOM2 监听 storage 事件监听,不能使用 DOM0 监听,最新 Chrome 不支持。

StorageEvent

当前页面使用的 storage 被其他页面修改时会触发 StorageEvent 事件。

事件在同一个域下的不同页面之间触发,即在 A 页面注册了 storge 的监听处理,只有在跟 A 同域名下的 B 页面操作 storage 对象,A 页面才会被触发 storage 事件

事件对象包含以下属性:

Property Type Description
target 只读 [EventTarget] 事件目标 (DOM 树中的最大目标)
type 只读 [DOMString] 事件的类型
bubbles 只读 [Boolean] 事件通常是否会出现冒泡
cancelable 只读 [Boolean] 事件是否可取消
key 只读 [DOMString] (string) 键更改时
oldValue 只读 [DOMString] (string) 正在更改键的旧值
newValue 只读 [DOMString] (string) 正在更改键的新值
url 只读 DOMString 键更改的文档的地址
storageArea 只读 [Storage] 受影响的存储对象
// 在一个标签页中设置localStorage
window.localStorage.setItem("sendMessage", "Hello,Bro!");
// 在同源下另一个或所有的标签页下所有数据 监听storage 事件
window.addEventListener("storage", event => {
  console.log(e.key);
  console.log(e.oldValu);
  console.log(e.newValue);
  if (e.key === "sendMessage") {
    console.log(e.newValue); // Hello,Bro!
  }
});

3.window.open 配合 window.postMessage

window.postMessage() 方法可以安全地实现跨源通信。

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

  • message:要发送的消息,可以是任意类型的数据。
  • targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"_"(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用 postMessage 传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的 origin 属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的 targetOrigin,而不是 _。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
  • transfer:可选的,是一串和 message 同时传递的 Transferable 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

targetOrigin 值可以是以下任意一个:

  • 一个 URI,表示该 URI 所表示的源。
// 在一个标签页中发送消息
const baidu = window.open("baidu.com");
baidu.postMessage("Hello,Bro!", "*");
// 在window.open()打开的另一个标签页中接受消息
window.addEventListener("message", event => {
  console.log("Received", event.data); // Received Hello,Bro!
});

4.SharedWorker(注意兼容性)

创建一个执行指定 url 脚本的共享 web worker。如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。

// worker.js
onconnect = function (e) {
  var port = e.ports[0];

  port.addEventListener("message", function (e) {
    var workerResult = "Result: " + e.data;
    port.postMessage(workerResult);
  });

  port.start(); // 使用addEventListener 要手动加上. 如果使用onmessage则可以省略
};
// 在不同页面中创建共享 web worker
this.shareWorker = new SharedWorker("./worker.js");
// 接受信息
this.shareWorker.port.onmessage = e => {
  console.log(e.data);
};
// 发送信息,可以传递任何东西
this.shareWorker.port.postMessage({
  type: "notifyTab",
  payload: {}
});

5.IndexedDB 定时器轮询,cookie 定时器轮询,Websocket 等



总结

  • BroadcastChannel 实时广播,给所有打开的标签页发送通知
  • StorageEvent 适合场景用到共享持久存储相关
  • window.open 配合 window.postMessage 可以跨源