如何实现 B 站多个窗口情况下同时退出&登陆?

image.png

  1. 今天正好接到一个需求,要求网站做到类似 B 站这样,当有多个窗口时,一旦某个窗口退出,其他窗口同步退出。
  2. 我观察到的现象是,一个窗口退出以后,其他窗口会同步发送一个网络请求。

问题:其他窗口是如何监听到另一个窗口的退出动作的呢?

题主补充(2023/6/6):最后采用设置 localstorage,并且 在 app.vue 里加载下面函数,完成需求

window.addEventListener("storage",(e)=>{
if(e.key=一些判断){
//TODO:触发页面重新刷新的逻辑
}

})
阅读 2.6k
2 个回答

1.WebSocket
html5提供的全双工通讯的协议,浏览器和服务器之间建立一条不受限的双向通信的通道,双向数据传输,需要服务端支持。

使用方法可参考:nodejs搭建websocket服务实现多页面通信

2.StorageEvent
简单有效的方式,页面A通过storage存储消息,页面B监听storage即可捕获消息。

pageA.html:

<body>
<input type="text" id="msg">
<button id="send">发送</button>
<script>
    send.onclick = function () {
        let msg = document.getElementById("msg").value
        localStorage.setItem('message', JSON.stringify({
            message: msg,
            from: 'pageA.html',
            date: Date.now()
        }))
    }
</script>
</body>

pageB.html:

<body>
收到了消息:<span id="text"></span>
<script>
    window.addEventListener('storage', function (e) {
        // console.log(e.key, e.newValue, e.oldValue)
        let msg = JSON.parse(e.newValue)
        document.getElementById('text').innerText = msg.message
    })
</script>
</body>

3.postMessage
支持跨域通信,需要获取打开窗口的句柄(对象),建立联系,安全地实现跨源通信。

pageA.html:

<body>
<button id="openw">打开B页面</button>
<br>
<input type="text" id="msg">
<button id="send">发送</button>
<script>
    let newWindow = null

    openw.onclick = function () {
        console.log(333)
        newWindow = window.open('pageB.html')
    }

    send.onclick = function () {
        let msg = document.getElementById("msg").value
        if (newWindow)
            newWindow.postMessage(msg)
    }
</script>
</body>

pageB.html:

<body>
收到了消息:<span id="text"></span>
<script>
    window.addEventListener('message', function (e) {
        document.getElementById('text').innerHTML = e.data
    })
</script>
</body>
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postM...

4.SharedWorker
实现一个在浏览器后台运行的共享worker,页面通过该worker进行连接和信息的传递。同源

本地不支持,需要放到服务器。( webstorm可在浏览器中打开,vscode使用live server插件)测试。

worker.js:

let portList = [];

onconnect = function (e) {
    let port = e.ports[0];
    if (portList.indexOf(port) < 0)
        portList.push(port);
    port.onmessage = function (e) {
        boardCast(e.data);
    };
    port.start();
};

function boardCast(data) {
    portList.forEach(port => port.postMessage(data));
}

pageA.html:

<body>
<input type="text" id="msg">
<button id="send">发送</button>
<script>
    if (!!window.SharedWorker) {
        let myWorker = new SharedWorker("worker.js");

        send.onclick = function () {
            let msg = document.getElementById("msg").value
            myWorker.port.postMessage(msg);
        }

        myWorker.port.start()

    } else {
        alert("当前浏览器不支持webworker!")
    }
</script>
</body>

pageB.html:

<body>
收到了消息:<span id="text"></span>
<script>
    if (!!window.SharedWorker) {
        let myWorker = new SharedWorker("worker.js");

        myWorker.port.onmessage = function (e) {
            document.getElementById('text').innerText = e.data
        }

        myWorker.port.start()

    } else {
        alert("当前浏览器不支持webworker!")
    }
</script>
</body>
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker

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

pageA.html:

<body>
<input type="text" id="msg">
<button id="send">发送</button>
<script>
    let channel = new BroadcastChannel('BroadcastChannel-test')
    send.onclick = function () {
        let msg = document.getElementById("msg").value
        channel.postMessage(msg)
    }
</script>
</body>

pageB.html:

<body>
收到了消息:<span id="text"></span>
<script>
    let channel = new BroadcastChannel("BroadcastChannel-test")
    channel.addEventListener("message", function (e) {
        document.getElementById('text').innerText = e.data
    })
</script>
</body>
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Broadcast_Ch...

6.MessageChannel
创建一个新的消息通道,并通过它的两个MessagePort 属性发送数据。常见场景iframe通信。

pageA.html:

<body>
<input type="text" id="msg">
<button id="send">发送</button>
<p>收到iframe消息:<span id="text"></span></p>
<iframe src="pageB.html" width='640' height='320'></iframe>
<script>
    let iframe = document.querySelector('iframe');

    send.onclick = function () {
        let channel = new MessageChannel();
        channel.port1.onmessage = onMessage
        let msg = document.getElementById("msg").value
        iframe.contentWindow.postMessage(msg, '*', [channel.port2]);
    }

    function onMessage(e) {
        document.getElementById('text').innerHTML = e.data
    }
</script>
</body>

pageB.html:

<body>
<input type="text" id="msg">
<button id="send">发送</button>
<p>收到main消息:<span id="text"></span></p>
<script>
    window.addEventListener('message', onMessage);

    let port = null

    function onMessage(e) {
        document.getElementById('text').innerHTML = e.data;
        port = e.ports[0]
    }

    send.onclick = function () {
        let msg = document.getElementById("msg").value
        if (port)
            port.postMessage(msg);
    }
</script>
</body>
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel

这种情况, 可以在浏览器客户端使用 StorageEvent 或者 BroadcastChannel 来通信, 获取到客户的退出事件.

也可以直接请求服务器, 查询用户登录状态, 如果已退出, 则做某些操作.

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏