利用localStorage多页面通信解决了一个问题

580 阅读4分钟

在做我司商城内嵌入一个即使通讯的项目的时候,遇到了一个比较棘手的问题。在商城页点击客服聊天,浏览器会新打开一个tab页(需求如此),目前由于后端代码的限制,后端老哥要求我前端这边永远只能打开一个聊天tab页,否则就会引发一个bug。就是后打开的tab页收不到消息,客服发的消息都会发到最先打开的聊天tab页中,不知道他们后端怎怎么造成的这个现象,我只能先按照后端老哥的要求去实现符合后端逻辑的情况。

最开始还是比较顺利,没想那么多。因为使用了window.open这个api,所以大概搜了一下怎么用,就开搞了,写完之后我还在掘金写了一篇总结juejin.cn/post/702875…

大概实现就是用一个变量接收window.open返回来的新页面的window对象,然后如果用户再次点击客服聊天的时候,我判断如果之前那个变量存在就是调用他的focus方法就可以了(还有一些跟业务逻辑相关的细节暂且不表),这样就可以打开之前的tab页而不是新开了,当然你可以每次调用open的时候传递相同的第二个参数也能达到这个效果,但这样会涉及到聊天页面的reload,这是我想要避免的(其实也没事,始终保持一个tab页就行)。

上面的问题解决了之后我就发现一个新的严重的问题就是,在商城A页面打开了聊天B页面,那么如果我在A页面再继续打开一个别的商品的页面,这个时候页面相当于重新加载了,新加载的页面如果再去点击客服聊天然后用window.open去打开页面的时候,返回的是一个全新的window对象了,我们无法在这个新的页面去打开 或者 刷新之前打开的B页面,这个时候我想了很多办法,比如想办法把B页面的引用使用localStorage缓存一下,很显然不可以,因为B的引用是window对象,是根本没法stringify的。然后又想了一个监听商城tab页 visible的办法,比如商城打开了一个新的连接并且该链接不是聊天页的时候,那么商城就会触发visible隐藏事件,在触发的事件内部调用B的关闭方法,很明显的问题就是,虽然这种方法可以解决新跳转别的页面可以关闭聊天页,但正常的tab切换也会导致聊天页关闭,然后通过这个方案,我想到了第三种办法。

每次聊天页打开,我都设置一个状态用来记录聊天状态。

localStorage.setItem('chatState','true')

每次打开商城都会执行如下代码:

    var chatState = localStorage.getItem('chatState');
    if(chatState === 'true'){
        localStorage.setItem('chatState','false')
    }
      // 上面的代码在新开标签页去点击客服聊天的时候执行
      // 下面的代码在每个商城页加载的时候执行
    window.addEventListener("storage",function(event){
        if(event.newValue === 'false'){
            chatTabIns && chatTabIns.close()
        }
    });
    

上面的storage最好的一点就是,你在本页面去设置,本页面不会触发,而是会触发其他页面的监听,所以当你新开的标签页检测到你开过聊天页之后,你可以设置为false,这样就会触发上一个商城页的监听,在上一个商城页就能成功拿到上一个商城页打开的聊天页引用了,就能关掉了,所以看到的现象就是在新的跳转页打开聊天,之前打开的聊天页自动关闭,新的标签页打开,符合了后端的要求

实际逻辑代码:

    
openChat: function (chatType) {
    if (chatTabIns && !chatTabIns.closed && chatTabIns.location.href.indexOf('/customerLogin') > -1 && chatType === _chatType) {
        // 当前正在聊天中
        console.log('相同TYPE且聊天tab页面已打开情况,打开聊天tab页,不重新执行代码')
        chatTabIns.focus();
    } else {
        console.log('TYPE不同或没有打开聊天tab页的情况,打开聊天tab页,重新执行代码')
        // 判断全局缓存中是否打开了聊天页,如果打开了设置为false,触发上一个页面注册的storage事件,这里属于新开标签页的情况,一律进行全局检索判断
        var chatState = localStorage.getItem('chatState');
        if(chatState === 'true'){
            localStorage.setItem('chatState','false')
        }
        if (chatType === 'g=10060646') {
            // 客服组
            chatTabIns = window.open(context_path + "/v2/imChat/customerLogin?flag=0", 'chatTabInsName')
            _chatType = chatType
        } else if (chatType === 'g=10085621') {
            // 医生组
            chatTabIns = window.open(context_path + "/v2/imChat/customerLogin?flag=1", 'chatTabInsName')
            _chatType = chatType
        }
    }
}

window.addEventListener("storage",function(event){
    if(event.newValue === 'false'){
        chatTabIns && chatTabIns.close()
    }
});

然后在chatTabIns内部:

localStorage.setItem('chatState','true')
window.onbeforeunload = function (e) {
    localStorage.setItem('chatState','false');
};