实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。
Plan 1. LocalStorage
原理:localStorage
中支持监听storage
变化时的事件,并且只有在不同标签页面且同源网页的时候才会触发。
实现方式:正常使用localstorage,在需要监听的位置写上如下代码:
window.addEventListener("storage", (e) => {
console.info("localStorage发生变化:", e)
})
Plan 2. PostMessage
优点:可以安全地实现跨源通信。
实现方式:【postMessage 菜鸟】
- 发送
var receiver = document.getElementById('receiver').contentWindow;
var btn = document.getElementById('sendMessage');
btn.addEventListener('click', function (e) {
e.preventDefault();
let val = 'Hello'
receiver.postMessage(val, "http://example.org:8080");
});
- 接收
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
// For Chrome, the origin property is in the event.originalEvent
let origin = event.origin || event.originalEvent.origin;
//示例,这里是发送方的url
let url = 'http://example.org:8080'
if (origin !== url) return;
// ...
}
小Tips: chrome浏览器中,URL 的窗口的消息的 targetOrigin
参数当前被错误解释,使得将导致发送消息的唯一值为 "*"
。由于此值是不安全的,当目标窗口可以导航到其他地方的恶意网站,建议 postMessage 不用于与 chrome。
Plan 3. ShareWorker
原理:shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色,由所有同源页面共享,然后通过这个共享的线程来实现数据的交换。
实现方式:
1) 新建worker.js
// worker.js
const set = new Set()
onconnect = event => {
const port = event.ports[0]
set.add(port)
// 接收信息
port.onmessage = e => {
// 广播信息
set.forEach(p => {
p.postMessage(e.data)
})
}
// 发送信息
port.postMessage("worker广播信息")
}
2) page1
<script>
const worker = new SharedWorker('./worker.js')
worker.port.onmessage = e => {
console.info("page1收到消息", e.data)
}
</script>
3) page2
<script>
const worker = new SharedWorker('./worker.js')
let myBtn= document.getElementById("myBtn");
let num = 0;
myBtn.addEventListener("click", () => {
worker.port.postMessage(`page2发送的消息:${num++}`)
})
</script>
兼容性:【SharedWorker MDN】