跨页面通信的几种方法

Author Avatar
zzz1220 4月 14, 2021
  • 在其它设备中阅读本文章
👉👉本文共1.1k字📘 读完共需4分钟⌚

原来,跨页面通信有这这么多方法

同源页面之间的通信

localStorage

这个大家都很熟悉了,但是他还有一个storage事件,之前没接触过
当 LocalStorage 变化时,会触发storage事件。利用这个特性,我们可以在发送消息时,把消息写入到某个 LocalStorage 中;然后在各个页面内,通过监听storage事件即可收到通知。

// 一个页面监听事件
window.addEventListener('storage', function (e) {
    if (e.key === 'ctc-msg') {
        const data = JSON.parse(e.newValue);
        const text = '[receive] ' + data.msg + ' —— tab ' + data.from;
        console.log('[Storage I] receive message:', text);
    }
});
// 另外一个页面set数据,这里加上事件戳是因为只有真正改变才会触发storage
mydata.st = +(new Date);
window.localStorage.setItem('ctc-msg', JSON.stringify(mydata));

IndexedDB

不多介绍了,就是简单的轮询

Service Worker

Service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。这个 API 旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用采取来适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。
Shared Worker 在实现跨页面通信时的问题在于,它无法主动通知所有页面,因此,我们会使用轮询的方式,来拉取最新的数据。思路如下:
让 Shared Worker 支持两种消息。一种是 post,Shared Worker 收到后会将该数据保存下来;另一种是 get,Shared Worker 收到该消息后会将保存的数据通过postMessage传给注册它的页面。也就是让页面通过 get 来主动获取(同步)最新消息。具体实现如下:
首先,我们会在页面中启动一个 Shared Worker,启动方式非常简单:

// 构造函数的第二个参数是 Shared Worker 名称,也可以留空
const sharedWorker = new SharedWorker('../util.shared.js', 'ctc');

然后,在该 Shared Worker 中支持 get 与 post 形式的消息:

/* ../util.shared.js: Shared Worker 代码 */
let data = null;
self.addEventListener('connect', function (e) {
    const port = e.ports[0];
    port.addEventListener('message', function (event) {
        // get 指令则返回存储的消息数据
        if (event.data.get) {
            data && port.postMessage(data);
        }
        // 非 get 指令则存储该消息数据
        else {
            data = event.data;
        }
    });
    port.start();
});

之后,页面定时发送 get 指令的消息给 Shared Worker,轮询最新的消息数据,并在页面监听返回信息:

// 定时轮询,发送 get 指令的消息
setInterval(function () {
    sharedWorker.port.postMessage({get: true});
}, 1000);

// 监听 get 消息的返回数据
sharedWorker.port.addEventListener('message', (e) => {
    const data = e.data;
    const text = '[receive] ' + data.msg + ' —— tab ' + data.from;
    console.log('[Shared Worker] receive message:', text);
}, false);
sharedWorker.port.start();

最后,当要跨页面通信时,只需给 Shared Worker postMessage即可:

sharedWorker.port.postMessage(mydata);

注意,如果使用addEventListener来添加 Shared Worker 的消息监听,需要显式调用MessagePort.start方法,即上文中的sharedWorker.port.start();如果使用onmessage绑定监听则不需要。

BroadCast Channel

Broadcast Channel API 可以实现同 源 下浏览器不同窗口,Tab页,frame或者 iframe 下的 浏览器上下文 (通常是同一个网站下不同的页面)之间的简单通讯。

简单的api展示:

// 连接到广播频道,在多个页面注册同一个频道
var bc = new BroadcastChannel('test_channel');
// 发送简单消息的示例
bc.postMessage('This is a test message.');
// 简单示例,用于将事件打印到控制台
bc.onmessage = function (ev) { console.log(ev); }
// 断开频道连接
bc.close()

非同源页面之间的通信

利用中间iframe作为brideg

使用一个用户不可见的 iframe 作为“桥”。由于 iframe 与父页面间可以通过指定origin来忽略同源限制,因此可以在每个页面中嵌入一个 iframe (例如:http://sample.com/bridge.html),而这些 iframe 由于使用的是一个 url,因此属于同源页面,其通信方式可以复用上面第一部分提到的各种方式。

/* 业务页面代码 */
window.addEventListener('message', function (e) {
    // …… do something
});

/* 业务页面代码发信息 */
window.frames[0].window.postMessage(mydata, '*');

利用websocket

利用后端新开一个websocket服务,多个页面都接受服务端的推送,也可以实现同步状态。


原文地址,本文稍作删改:
https://juejin.cn/post/6844903811232825357