浏览器跨标签页通信
大家都知道,浏览器可以同时打开多个标签页的,而且在实际的使用场景中,打开多个标签页是非常常见的情况。比如说某个网站的列表页面,点击之后新开标签页展示详情,用户在详情里面进行了操作,列表标签页也需要更新数据。总的来说:多个标签页之间通信是很有必要的。
-
Broadcast Channel(广播频道)
用于同源(相同协议、主机和端口号)浏览器的多个标签页之间广播通信,每个 Broadca Channel 实例都属于一个特定的通道名称,并且可以由具有该名称的页面监听。(同源:指该方法无法跨域通信。)
-
localStorage
是同源(相同协议、主机和端口号)浏览器的多个标签页共享数据的存储空间,可以实现多标签之间的通信。(session 是会话级的存储空间,每个标签页都是单独的。)
a.html
function setStorage(){
if(!localStorage.getItem('hasStorage')){
window.open('/b.html');
localStorage.setItem('hasStorage',1);
}
setTimeout(()=>{
localStorage.setItem('man','摸鱼君');
},3000);
}
b.html
window.onunload = () =>{
localStorage.removeItem('hasStorage');
localStorage.removeItem('man');
}
const app = document.getElementById('app');
window.addEventListener('stroage', e=>{
const app = document.getElementById('app');
app.innerText = e.newValue;
})
-
SharedWorker
是WebWorker 的一种,允许同源(相同协议、主机和端口号)浏览器的多个标签页共享同一个全局上下文(比如多个 windows,iframes 和 workers)。即可以用来在多个标签页之间共享数据,一个标签页对数据进行修改,其他标签页可以立即看到更新。但必须保证这些标签页都是同源的。
// sharedWorker.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("SharedWorker发出信息");
}
// A.html
<script>
const sharedWorker = new SharedWorker('./sharedWorker.js');
sharedWorker.port.onmessage = e => {
console.info("A收到:", e.data);
}
</script>
<script>
// B.html
const sharedWorker = new SharedWorker('./sharedWorker.js');
let btnB = document.getElementById("btnB");
btnB.addEventListener("click", () => {
sharedWorker.port.postMessage("B发出消息");
})
</script>
以上代码是最简单的 A、B 两个页面发送消息的实现demo。SharedWorker 初始化完成后:A 页面首先收到“A收到:SharedWorker发出信息”,然后 B 页面点击按钮,发送消息给 A页面,A 页面收到“A收到:B发出消息”。
-
WebSocket
是一种全双工(full-duplex)通信协议,允许客户端和服务器之间实时通信,享受平等关系(比如说聊天工具)。如果两个标签页都与服务器建立了 WebSocker 连接,则可以直接进行实时通信。
-
postMessage
两个需要交互的 tab 页面具有依赖关系。如 A 页面中通过 JavaScript 的 window.open 打开 B 页面,或者 B 页面通过 iframe 嵌入到 A 页面,这种情形最简单,可以通过 HTML5 的 window.postMessage API 完成通信,用于 postMessage 函数是绑定在 window 全局对象下,因此通信的页面中必须有一个页面(如 A 页面)可以获取另一个页面(如 B 页面)的 window 对象,这样才可以完成单向通信;B 页面无需获取 A 页面的 window 对象,如果需要 B 页面对 A 页面的通信,只需要在 B 页面侦听 message 事件,获取事件中传递的 source 对象,该对象即为 A 页面 window 对象的引用:
// B页面
window.addEventLister("message", (e) => {
let { data, source, origin } = e;
source.postMessage("message echo", "/");
});
postMessage 的第一个参数为消息实体,它是一个结构化对象,即可以通过JSON.stringify 和 JSON.parse函数还原的对象;第二个参数为消息发送范围选择器,设置为"/"意味着只发送消息给同源的页面,设置"*"则发送全部页面。
-
定时器 setInterval+cookie
在页面 A 设置一个使用 setInterval 定时器不断刷新,检查 cookies 的值是否发生变化,如果变化就进行刷新的操作
由于 cookies 是在同域可读的,所以在页面 B 审核的时候改变 Cookies 的值,页面 A 自然是可以拿到的
这样做确实可以实现想要的功能,但是这样方式相当浪费资源。虽然在这个性能过盛的时代,浪费不浪费也感觉不出来,但是这种实现方案,确实不够优雅。