一. 实现多标签通信的几种方案
1. BroadcastChannel
引 MDN:
Broadcast Channel API 可以实现同 源 下浏览器不同窗口,Tab页,frame或者 iframe 下的 浏览器上下文 (通常是同一个网站下不同的页面)之间的简单通讯。
1) 用法:
// 连接到广播频道
var bc = new BroadcastChannel('test_channel');
// 发送消息
bc.postMessage('This is a test message.');
// 接收消息
bc.onmessage = function (ev) { console.log(ev); }
// 断开频道连接
bc.close()
2) 兼容性:
3) 优点:
原生接口,可直接使用;接口简单,使用方便。
4) 缺点:
由兼容性可知,BroadcastChannel 接口不被 IE 和 Safari 支持,如果需要使用 IE 和 Safari,这个接口就不能用了。
2. WebSocket
WebSocket 方案需要后端配合,页面创建 socket 与后端通信,借此实现标签页之间的通信。具体实现不在此赘述。
缺点:
需要后端配合,不够便捷。
3. SharedWorker
引 MDN:
SharedWorker
**接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域,SharedWorkerGlobalScope
(en-US) 。
注意:如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。
1) 用法:
let worker = new SharedWorker('sharedworkers.js');
// 传递strat指令, 启用端口
worker.port.postMessage('start');
// 接收子线程的数据
worker.port.onmessage = function (val) {
timeDom.innerHTML = val.data
}
// sharedworkers.js
let a = 0;
onconnect = function (e) {
// 通过 e.ports 拿到 port
var port = e.ports[0];
// port.onmessage 监听父线程的消息
port.onmessage = function () {
// port.postMessage 向父线程传递消息
port.postMessage(a++)
}
}
2) 兼容性:
3) 优点:
Worker 运行于独立的线程,对页面性能比较友好,可以执行运算量比较大的计算。
4) 缺点:
需要单独的 worker 文件;调试比页面上的 js 麻烦;兼容性差,IE不支持,Safari 曾经支持过,未来也可能支持,但是现在不支持,不可靠。
4. window.postMessage
引 MDN:
window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数
Document.domain
设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
从广义上讲,一个窗口可以获得对另一个窗口的引用(比如
targetWindow = window.opener
),然后在窗口上调用targetWindow.postMessage()
方法分发一个MessageEvent
消息。接收消息的窗口可以根据需要自由处理此事件 (en-US)。传递给 window.postMessage() 的参数(比如 message )将 通过消息事件对象暴露给接收消息的窗口 。
1) 用法:
发送方:
/*
* A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:
*/
var popup = window.open(...popup details...);
// 如果弹出框没有被阻止且加载完成
// 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
"https://secure.example.net");
// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
popup.postMessage("hello there!", "http://example.org");
function receiveMessage(event)
{
// 我们能相信信息的发送者吗? (也许这个发送者和我们最初打开的不是同一个页面).
if (event.origin !== "http://example.org")
return;
// event.source 是我们通过window.open打开的弹出页面 popup
// event.data 是 popup发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
接收方:
/*
* 弹出页 popup 域名是<http://example.org>,以下是script标签中的代码:
*/
//当A页面postMessage被调用后,这个function被addEventListener调用
function receiveMessage(event)
{
// 我们能信任信息来源吗?
if (event.origin !== "http://example.com:8080")
return;
// event.source 就当前弹出页的来源页面
// event.data 是 "hello there!"
// 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把event.source
// 作为回信的对象,并且把event.origin作为targetOrigin
event.source.postMessage("hi there yourself! the secret response " +
"is: rheeeeet!",
event.origin);
}
window.addEventListener("message", receiveMessage, false);
2) 兼容性:
3) 优点:
兼容性好,安全。
4) 缺点:
需要拿到其他窗口的句柄,一般是 open、opener、iframe,其他情况无法使用。
5. localStorage
localStorage 可以在同源的窗口间共用,js 提供了监听 localStorage 变化的接口,以localStorage 为管道,可以实现标签间的通信。
1) 用法:
发送方:
localStorage.setItem('a', '111')
接收方:
window.onstorage = (e) => {console.log(e)}
// 或者这样
window.addEventListener('storage', (e) => console.log(e))
2) 兼容性:
3) 优点:
兼容性好。
4) 缺点:
间接通过数据变化获取信息,不够直接,用起来比较麻烦。
二. 共享 Session
需求:
多标签页共享登录凭证,关闭所有标签页后,清除登录凭证。
sessionStorage
浏览器自带的 sessionStorage 可以实现关闭窗口时清空凭证,但是无法实现多标签间的共享。
SharedSession
基于 sessionStorage 和上文提到的 localStorage 实现多标签通信,笔者实现了多标签间的共享 session 机制。
用法:
npm i shared-session
#or
yarn add shared-session
import SharedSession from 'shared-session'
//监听数据变化
SharedSession.listen((data) => {
console.log(data)
})
//获取数据
SharedSession.getItem('key').then((value) => {
console.log(value)
})
//更新数据
SharedSession.setItem('key', 'value')
//删除某个字段
SharedSession.removeItem('key')
//清除所有数据
SharedSession.clear()