5

用户在一个标签页已登录,再打开多个标签页,此场景下将token保存在sessionStorage中将会带来很差的用户体验,每次开启一个标签页都会要求用户重新登录。

sessionStorage顾名思义是针对一个session的数据存储,生命周期为当前窗口,一旦窗口关闭,那么存储的数据将被清空。最后还有一个很主要的区别同一浏览器的相同域名和端口的不同页面间可以共享相同的 localStorage,但是不同页面间无法共享sessionStorage的信息。

解决:
在index.html中增加以下代码,复制全部sessionStorage内资料

(function(){
    // 判断当前页面是否存在sessionStorage
    if (!sessionStorage.length) {
        // 这个调用能触发目标事件,从而达到共享数据的目的(若不存在则加上一个localStorage Item,key=getSessionStorageData)
        localStorage.setItem('getSessionStorageData', Date.now());
    };

    // 该事件是核心,增加window监听事件 
    window.addEventListener('storage', function(event) {
         // 已存在的标签页会收到这个事件,如果监听到的事件key是getSessionStorageData
         if (event.key == 'getSessionStorageData') {
             // 再新增一个localStorage Item,key=sessionStorageData,value就是当前的sessionStorage
            localStorage.setItem('sessionStorage', JSON.stringify(window.sessionStorage));
            // 删除localStorage中key=sessionStorageData的item
            localStorage.removeItem('sessionStorage');

        } 
        if (event.key == 'sessionStorageData' && !sessionStorage.length) {
            // 新开启的标签页会收到这个事件,把sessionStorageData的资料parse出来
            const data = JSON.parse(event.newValue);
            //  赋值到当前页面的sessionStorage中
            for (key in data) {
                window.sessionStorage.setItem(key, data[key]);
            }
        }
    });
})();

整体流程:
1、判断页面是否存在sessionStorage,并加上store事件的监听;
2、若不存在,就把原本的sessionStorage内的资料复制到localStorage中并取名为sessionStorageData;
3、若storage事件监听到key为sessionStorageData的事件,就把内容通过for赋值到页面sessionStorage中;

为什么复制到localStorage后又直接删除?
因为window添加了监听事件

window.addEventListener('storage', ..)

这就意味着每次storage事件被触发的时候都会执行,所以当setItem('sessionStorage')的时候,也会接收到事件,
代表着

window.localStorage.removeItem('sessionStorage')

其实是发生在sessionStorage被写入之后的事情了,所以可以同时写入和删除,不留下localSorage的记录。

新开的页面为什么会有sessionStorage可以提供复制?
查询API(Using the Web Storage API|MDN)后知道,各标签只要是同域名,有对storage动作时全部都会被连动,storage事件可以做到同域名的状态监测。

storage事件的测试
随便开两个同域名的标签页后,分别开启开发者工具:
1、在A标签页下

window.addEventListener('storage', (event) => console.log(event));

2、在B标签页下

window.localStorage.setItem('storageTest', 'test');

3、再回头看A标签页,就会发现已经被监听到storage事件了。
通过这样的事件监听,就可以达到由localSorage传递sessionStorage的目的。

关于登出
登出时同步清除所有标签页的sessionStorage并reload页面

(function(){
    // 判断当前页面是否存在sessionStorage
    if (!sessionStorage.length) {
        // 这个调用能触发目标事件,从而达到共享数据的目的(若不存在则加上一个localStorage Item,key=getSessionStorageData)
        localStorage.setItem('getSessionStorageData', Date.now());
    };

    // 该事件是核心,增加window监听事件 
    window.addEventListener('storage', function(event) {
         // 已存在的标签页会收到这个事件,如果监听到的事件key是getSessionStorageData
         if (event.key == 'getSessionStorageData') {
             // 再新增一个localStorage Item,key=sessionStorageData,value就是当前的sessionStorage
            localStorage.setItem('sessionStorage', JSON.stringify(window.sessionStorage));
            // 删除localStorage中key=sessionStorageData的item
            localStorage.removeItem('sessionStorage');

        } 
        if (event.key == 'sessionStorageData' && !sessionStorage.length) {
            // 新开启的标签页会收到这个事件,把sessionStorageData的资料parse出来
            const data = JSON.parse(event.newValue);
            //  赋值到当前页面的sessionStorage中
            for (key in data) {
                window.sessionStorage.setItem(key, data[key]);
            }
        }
        // ===== 加下面这段 =====
        if (event.key === 'logout') {
            // 接收到logout事件,进行sessionStorage的清除和页面reload
            window.sessionStorage.clear();
            window.location.clear();
        }
    });
})();

其他
即使这样看起来ok,但还是有缺陷
1、Chrome,FireFox的恢复分页功能会将sessionStorage的资料一起恢复,这可能会导致一些安全性问题(不过相较于localStorage,这应该不算问题)
2、这些数据是标签页被开启的同时获取的,意味着如果你的sessionStorage储存着经常被变动的资料,必须再写一些事件去拦截和复制,因为storage事件不会监听到其他页面的sessionStorage变动.


soso辉
84 声望2 粉丝