那些被忽略但很好用的 Web API / BroadcastChannel

里长办公室广播:张君雅小妹妹,恁兜欸泡面已经煮好了!

前两天已经认识了 PostMessage 和建立专属频道的 MessageChannel,它们都是进行点对点的沟通,但如果想要一次跟多个页面沟通时怎麽办了,这时 BroadcastChannel 就能派上用场了。


BroadcastChannel

BroadcastChannel 就如同一个无线对讲机系统,讯息是播放在一个广播频道中,任何页面只要取得频道的频率就可以在无线电中发送/接收讯息。

# 建立频道

MessageChannel 一样,BroadcastChannel 本身也是一个 Class,只要透过关键字 new 就能建立一个广播频道,不过这次我们要传入一个字串来当作广播频道的名称,未来其他页面才能藉由同样的字串来进入频道。

const channel = new BroadcastChannel("max_channel");

接着只要透过我们建立的广播频道送出讯息即可:

const channel = new BroadcastChannel("max_channel");
channel.postMessage("你已成功加入频道!", location.origin);

要收到讯息的话就用 BroadcastChannel 监听 message 即可:

const channel = new BroadcastChannel("max_channel");
channel.onmessage = function (event) {
  console.log(event.data);
};

 

# 频道沟通

这时候其他页面只要使用同样的频道名称建立一个 BroadcastChannel,并且一样透过该频道来传送/接收讯息,这样所有频道中的页面就都可以相互沟通了。

<!-- 这里是 main.html -->
<button onclick="sendMessage()">send message</button>
<iframe src="pageA.html" width="480" height="120"></iframe>
<iframe src="pageB.html" width="480" height="120"></iframe>
<script>
  const channel = new BroadcastChannel("max_channel");
  channel.onmessage = function (event) {
    console.log(event.data);
  };
  function sendMessage() {
    channel.postMessage("你已成功加入频道!", location.origin);
  }
</script>
<!-- 这里是 pageA.html -->
<button onclick="sendMessage()">send message</button>
<div class="output">pageA content</div>
<script>
  const output = document.querySelector(".output");
  const channel = new BroadcastChannel("max_channel");
  channel.onmessage = function (event) {
    const output = document.querySelector(".output");
    output.innerHTML = event.data;
  };
  function sendMessage() {
    channel.postMessage("pageA 发送讯息!", location.origin);
  }
</script>
<!-- 这里是 pageB.html -->
<button onclick="sendMessage()">send message</button>
<div class="output">pageB content</div>
<script>
  const channel = new BroadcastChannel("max_channel");
  channel.onmessage = function (event) {
    const output = document.querySelector(".output");
    output.innerHTML = event.data;
  };
  function sendMessage() {
    channel.postMessage("pageB 发送讯息!", location.origin);
  }
</script>

 

# 频道名称

不过上面这样的范例,会有一个令人诟病的地方,就是「频道名称」的同步问题,要是有其中某个页面打错频道名称,那就会连不上频道。或是如果想要更换频道名称的时候,就必须大家一起改,似乎是又点不太方便。

所以在主页面建立频道後,其实可以先用一般的 postMessage 将频道名称传给需要的页面:

<iframe src="pageA.html" width="480" height="120"></iframe>
<iframe src="pageB.html" width="480" height="120"></iframe>
<script>
  const allIframe = document.querySelectorAll("iframe");
  const channel = new BroadcastChannel("max_channel");

  // 发送频道名称
  allIframe.forEach((iframe) => {
    iframe.addEventListener("load", function () {
      this.contentWindow.postMessage(channel.name, location.origin);
    });
  });

  channel.onmessage = function (event) {
    console.log(event.data);
  };
</script>
<!-- 这里是 pageA.html -->
<div class="output">pageA content</div>
<script>
  let channel;
  window.addEventListener("message", function (event) {
    if (event.origin !== location.origin) return;
    channel = new BroadcastChannel(event.data);

    channel.onmessage = function (bc_event) {
      const output = document.querySelector(".output");
      output.innerHTML = event.data;
    };

    channel.postMessage("pageA 加入频道", location.origin);
  });
</script>
<!-- 这里是 pageB.html -->
<div class="output">pageB content</div>
<script>
  let channel;
  window.addEventListener("message", function (event) {
    if (event.origin !== location.origin) return;
    channel = new BroadcastChannel(event.data);

    channel.onmessage = function (bc_event) {
      const output = document.querySelector(".output");
      output.innerHTML = event.data;
    };

    channel.postMessage("pageB 加入频道", location.origin);
  });
</script>

补充:如果有页面想要与广播频道断开连结的话,只要拿建立的频道执行 close() 即可,关闭後频道还是存在,只是该页面不再接收频道的讯息。

 

这三天我们已经完全认识了 PostMessage,了解到它不但可以传送讯息,还可以建立私讯或群组的通讯模式,而这项技术其实时常会用在 Web Worker 中,但因为它的范畴有点庞大,所以这次的系列文章不会介绍到,有兴趣的朋友可以再自行研究~


<<:  [Day17] 一级函式

>>:  Alpine Linux Porting (1.11?)

[Day23] Scrum失败经验谈 – 以为什麽都不能动了!

不变,不代表会更快更好 要求不变,反而会花过多时间追求完美 在[Day22] Scrum失败经验谈 ...

[DAY 26] 分散式训练

前言 在上一章我们知道如何在一台机器上使用多张 GPU 来Training,这对我们 Train 大...

用python下载东西

其实不用安装requests就可以下载东西 python的urllib.request.urlope...

#11 Pandas教学3

Pandas汇入CSV档 # 载入pandas import pandas as pd if __n...

Day 23 - 重覆呼叫shioaji.Shioaji()产生的记忆体问题

因为看到有人反应,重覆登出登入,会造成记忆体使用量增加,这实在是让人太好奇了,所以就想来实测一下。但...