chrome浏览器多页签唯一关闭时自动注销

  • 整体思路
  • 多页签唯一思考
    • 探索window对象name的属性
      • window.name的特征
    • 页签唯一实现
  • 页签间共享信息思考
    • 页签数量的管理实现
  • 监听chrome浏览器页签关闭动作思考
    • 关闭动作监听实现
  • 自动注销思考
    • 注销代码实现
  • 测试出来的问题思考
    • 页签在打开状态的心跳实现
  • 总结

整体思路

技术背景:vue单页面应用
整体思路按如下问题展开:

  1. 多页签如何唯一?
  2. 页签唯一了,页签间如何共享信息?
  3. 页签间共享信息解决了,能否监听浏览器关闭动作?
  4. 浏览器关闭动作监听到了,如何自动注销?
  5. 自动注销实现了,是否还存在其他问题?

由上述问题可知,其中自动注销的最简单的实现,基本上就是清除浏览器记录的用户登录信息,一般都是清除相关的cookie。

多页签唯一思考

我们知道自动注销无非就是设置cookie的信息过期,实现自动清除用户的登录信息。用户在浏览器上对于同一系统只打开一个浏览器页签时,这样的实现是可以的。打开了多个页签的话,用此方式只能实现单点登出,即随意关闭一个页签导致其他页签全部注销了,这样的不符合需求和用户习惯的。我们现在的难题在于怎样识别是否只剩唯一一个页签。因此我们需要探索window对象的属性,找出可以标识页签的唯一的属性,这样就可以知道用户打开了哪些页签和关闭了哪些页签,以及最后只剩哪个页签。

探索window对象name的属性

我们通过网上查看window对象的相关属性,发现name属性可以标识页签的唯一

window.name的特征

  1. 每个页签都可以设置window.name,设置后的页签是独立的,无法打开相同window.name的页签;
  2. 可以直接赋值name,直接获取则返回当前页签名称,window.name默认是空字符串
  3. window.name的数据类型是string,传递其他类型的数据都会转成string;

页签唯一实现

在页面加载时,如果window.name为空,则设置为UUID ,非空则跳过,说明已设置。

//main.js里插入以下代码
const uuid = require('uuid')
if(window.name===''){window.name=uuid.v4();
}

页签间共享信息思考

浏览器的存储对象有cookies、sessionStorage、localStorage,这里我们根据作者”pengc“的博客:cookies、sessionStorage和localStorage解释及区别,得知chrome使用localStorage最优,使用cookies会浪费带宽。
使用 localStorage 可以创建永不过期的本地存储的 name/value 对,而且value只能为string类型。我们需要记录多个打开的页签window.name,因此需要构建一个数组,而且其中window.name不能重复,然后转成json字符串存储。在页签关闭时去删除对应数组的元素就实现了页签的数量管理。

页签数量的管理实现

使用 localStorage创建key为Page-uuid,value为数组转成的json字符串的键值对,围绕这个键值对实现三个核心方法:获取页签的数量方法getPageNum,记录页签的window.name方法setPageUuid,清除数组中的页签记录removePageUuid。

//可以写到utils.js
const PageUuidKey = 'Page-uuid';
//获取页签的数量方法
export function getPageNum() {//获取原有的数组的json字符串let pageStr=localStorage.getItem(PageUuidKey);if(pageStr){//转成json数组let pageList=JSON.parse(pageStr);if(pageList){//返回数组长度return pageList.length;}}//其他情况直接返回0return 0;
}
//记录页签的window.name
export function setPageUuid(_uuid) {//有效毫秒数,根据需要设置let validPeriod=4000//获取原有的数组的json字符串let pageStr=localStorage.getItem(PageUuidKey);if(pageStr){//转成json数组let pageList=JSON.parse(pageStr);if(pageList){//判断是否已设置过该window.nameif(isExistsKV(pageList,'name',_uuid)){//已设置过该window.name,则重新设置失效时间let index=getItemIndexByKeyValue(pageList,'name',_uuid);pageList[index].expireTime=new Date().getTime()+validPeriod}else{//未设置过该window.name,则插入记录pageList.push({name:_uuid,expireTime:new Date().getTime()+validPeriod});}//更新localStorage的多页签记录localStorage.setItem(PageUuidKey,JSON.stringify(pageList));}}else{//没有设置过,则新增localStorage的多页签记录localStorage.setItem(PageUuidKey,JSON.stringify([{name:_uuid,expireTime:new Date().getTime()+validPeriod}]));}
}
//清除数组中的页签记录
export function removePageUuid(_uuid) {//获取原有的数组的json字符串let pageStr=localStorage.getItem(PageUuidKey);if(pageStr){//转成json数组let pageList=JSON.parse(pageStr);if(pageList){//获取需要清除的记录的数组下标let index=getItemIndexByKeyValue(pageList,'name',_uuid);if(index!==-1){//清除页签记录pageList.splice(index,1);//更新localStorage多页签记录localStorage.setItem(PageUuidKey,JSON.stringify(pageList));}}}
}
// 判断数组是否存在该对象
export function isExistsKV(items, key,value) {for(var i=0,len=items.length;i<len;i++){if(items[i][key]===value){return true}}return false
}
// 根据键值对获取对应的元素下标
export function getItemIndexByKeyValue(items,  key,value) {for(var i=0,len=items.length;i<len;i++){if(items[i][key]===value){return i}}return -1
}

监听chrome浏览器页签关闭动作思考

这里不讨论其他浏览器的关闭监听,有需要可以看看作者”云_飞扬“的博客:js监听浏览器关闭事件(区分刷新和关闭,兼容IE9,10,11,Edge,Chrome和Firefox),必须要感谢网上众多大神给予的帮助。
通过网上查找资料得知,chrome浏览器刷新和关闭都会按顺序触发window.onbeforeunload事件和window.onunload事件,两个事件间的时差长短可以作为判断页签的动作属于关闭还是刷新,不超过5毫秒的(<=5)的是关闭,否则是刷新。本人的测试了很多次,大部分时间是这样的,小部分出现问题。一直不知道这个原理是什么,望有大神能慷慨解答下。
关闭动作我们可以监听到了,页签唯一也设置了,那如何得知打开的页签只剩一个了,那我们需要找出可以应用共享的存储对象,这样就可以把打开的页签记录到共享的存储对象,关闭的页签则进行清除。

关闭动作监听实现

//单页应用的home主页上插入以下代码
mounted() {//这里省略其他无关的代码。。。//监听刷新和关闭的相关事件window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))window.addEventListener('unload', e => this.unloadHandler(e))//监听页签切换,清除监测页签定时器,下文再述    document.addEventListener('visibilitychange',e => this.clearCheckPageUuid(e))
},
methods: {//这里省略其他无关的代码。。。//window.onbeforeunload事件处理beforeunloadHandler(e) {this.beforeUnloadTime = new Date().getTime();},//window.onunload事件处理unloadHandler(e) {let _gap_time = new Date().getTime() - this.beforeUnloadTime;  //判断是窗口关闭还是刷新if (_gap_time <= 5) {//关闭处理this.closeHandler();}},//关闭处理closeHandler() {if (getPageNum() <= 1) {//注销,下文再述   this.logout()} else {//关闭当前页面this.closeCurrentPage()}},//关闭当前页面closeCurrentPage(){//注册页签定时器,下文再述 window.clearInterval(registerPageUuidInterval)//监测页签定时器,下文再述 window.clearInterval(checkPageUuidInterval) //清除数组中的页签记录removePageUuid(window.name);}
},
destroyed() {//这里省略其他无关的代码。。。//注销,下文再述   this.logout()   //清除监听刷新和关闭的相关事件window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))window.removeEventListener('unload', e => this.unloadHandler(e))//清除页签切换的监听,下文再述 document.removeEventListener('visibilitychange',e => this.clearCheckPageUuid(e))//清除注册页签定时器,下文再述   window.clearInterval(registerPageUuidInterval)//清除监测页签定时器,下文再述   window.clearInterval(checkPageUuidInterval)
}

自动注销思考

由于项目的登录信息是存储在cookie上,因此自动注销的实现,就是在页签唯一且关闭时设置cookie的登录信息过期即可。

注销代码实现

import Cookies from 'js-cookie'
methods: {//这里省略其他无关的代码。。。//注销,TokenKey为登录信息的key,自行设置logout() {//清除所有的多页签记录localStorage.removeItem(PageUuidKey);//设置过期,清除登录的cookie信息Cookies.set(TokenKey, '', {path: '/',expires: 0});},
}

测试出来的问题思考

其实在项目测试的过程中发现一些意外的情况我们没法避免,就是可能出现该清除的多页签记录没有清除,导致已经关闭的页面localStorage里还有记录,然后就会导致cookie无法清除,因为页签不可能只剩一个了。哪如何得知localStorage的多页签记录是有效的(即页面是打开的)?本人暂时没找到javascript可以直接根据window.name判断页面是否在打开的状态。如有大神知晓望能指教。
于是本人根据做大数据集群的思维,想到可以利用分布式的心跳机制来监测页面是否在打开的状态。
其实核心的实现就在未写出来的注册页签定时器方法registerPageUuidInterval、监测页签定时器方法checkPageUuidInterval和清除监测页签定时器方法clearCheckPageUuid

页签在打开状态的心跳实现

相信大家注意到在记录页签的window.name的方法中多页签记录的数组元素不是string类型,而是对象类型。关键是这个对象有失效时间expireTime这个属性,利用这个属性我们定时T监听是否失效,失效则清除记录即可;还在打开的页面为了不失效,则定时t去注册延长失效时间ts;为了保证监测页签定时器只有一个,因此需要监听页签的切换。打开多个页签会有多个监测页签定时器同时运行,除了会导致性能问题,还有可能和注册页签定时器产生冲突,因此只有切换到的页签去启动监测页签定时器即可。

这里的时间关系为:2*t<=ts<=T,本文设置为 t =2000毫秒,ts=4000毫秒,T=4000毫秒,具体频率按项目实际设置即可,不宜过短过长,过短则影响浏览器性能,过长则会延时出更多问题。

//可以写到utils.js
export function removeTimeoutPageUuid() {let pageStr=localStorage.getItem(PageUuidKey);if(pageStr){let pageList=JSON.parse(pageStr);if(pageList){let newList = [];let now = new Date().getTime()pageList.forEach(item => {if(now - item.expireTime<0){newList.push(item)}});pageList = newListlocalStorage.setItem(PageUuidKey,JSON.stringify(pageList));}}
}
//单页应用的home主页上插入以下代码
//监测页签定时器方法
var checkPageUuidInterval= self.setInterval(()=>{//每4秒清空过时的removeTimeoutPageUuid()},4000);
//注册页签定时器方法
var registerPageUuidInterval= self.setInterval(()=>{//每2秒注册pageuuidsetPageUuid(window.name)},2000);
export default {//这里省略其他无关的代码。。。methods: {//这里省略其他无关的代码。。。//切换页签,清除监测页签定时器方法clearCheckPageUuid(e){if(document.visibilityState==='hidden'){window.clearInterval(checkPageUuidInterval)}else{checkPageUuidInterval=self.setInterval(()=>{//每4秒清空过时的removeTimeoutPageUuid()},4000);this.setUserInfo()}},},//这里省略其他无关的代码。。。
}

总结

本文的核心技术实现总结为window.nameuuid实现页签的唯一,localStorage实现页签间的信息共享,监听beforeunloadunload事件实现关闭动作触发,window.setInterval定时器、监听visibilitychange事件和心跳机制实现页签打开状态的监控。

chrome浏览器多页签唯一关闭时自动注销相关推荐

  1. 如何解决谷歌Chrome浏览器空白页的问题

    如何解决谷歌Chrome浏览器空白页的问题 谷歌Chrome浏览器突然不打开任何网页,无论是任何站点(如http://www.baidu.com), 还是Chrome浏览器的设置页面(chrome:/ ...

  2. fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件

    fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件 参考文章: (1)fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件 (2)https://www.cnblog ...

  3. js 点击按钮打开浏览器新页签,兼容版

    话不多说直接上代码,这是千辛万苦寻来的,帮助需要帮助的人. <html><head>点击按钮打开浏览器新页签</head><body><div o ...

  4. 浏览器tab页签上的title图标favicon.icon

    文章目录 浏览器tab页签上的title图标favicon.icon 什么是favicon? 如何查看favicon 在线生成favicon 浏览器tab页签上的title图标如何设置 浏览器tab页 ...

  5. 如何解决Chrome浏览器多功能搜索框搜索时不能调用百度搜索引擎

    2019独角兽企业重金招聘Python工程师标准>>> 如何解决Chrome浏览器多功能搜索框搜索时不能调用百度搜索引擎 http://www.baidu.com/s?ie=UTF- ...

  6. 自定义Chrome浏览器新建标签页为空白页

    Chrome是我最喜欢的用的浏览器,速度快,稳定,但是有一点不喜欢,就是新建标签页中间总是有些常用网址以及搜索框,像狗皮膏药一样,既然已经有标签栏了,我觉得这些完全多余,所以就动手弄了个扩展,把标签页 ...

  7. chrome浏览器不支持video标签,不能自动播放。

    问题:chrome浏览器不支持video标签的autoplay属性,网页刷新后不能自动播放视频. 处理: 方法一: 在video标签中加入muted="muted" 属性,但会让视 ...

  8. 【已解决】谷歌Chrome浏览器中如何打开或关闭网页的自动翻译功能?

    对于经常查阅各种外文资料的人来说,谷 歌翻译插件 必不可少.但是今天小编要跟大家说的不是 chrome插件 ,而是chrome自带的网页翻译功能.Chrome有自带整页翻译的功能,这个我相信很 对于经 ...

  9. 谷歌Chrome浏览器中如何打开或关闭网页的自动翻译功能?

    对于经常查阅各种外文资料的人来说,谷 歌翻译插件 必不可少.但是今天小编要跟大家说的不是 chrome插件 ,而是chrome自带的网页翻译功能.Chrome有自带整页翻译的功能,这个我相信很 对于经 ...

最新文章

  1. 十分钟完成Bash 脚本进阶!列举Bash经典用法及其案例
  2. c语言字符的内码是怎么表示,用C语言实现常见的三种中文内码转换
  3. php文件下载教学,php下载文件的代码示例
  4. mysql使用 BETWEEN AND 查询
  5. Linux vi的基本操作
  6. OpenAI新研究:通过非监督学习提升NLP任务表现
  7. 解决时间控件input不能选择的问题
  8. 裴礼文《数学分析中的典型问题与方法》P61~90
  9. 用axure绘制PHP工作流程图,如何用Axure绘制高质量的业务流程图?
  10. 使用机器学习算法打造一个简单的“微博指数”
  11. 【多功能改进】基于OpenCV图像采集的人脸识别网络推流及局域网无线控制系统(将图像在URL地址上输出,可做成网络摄像头,带识别框)
  12. 2017年保荐代表人胜任能力考试辅导教材 投资银行业务
  13. 一站式数字藏品收款系统开发解决方案
  14. 什么是联想能力?如何提高联想能力?
  15. TypeError: 'str' object cannot be interpreted as an integer
  16. 论文 | 图理论 | 2021年斯坦福大学Jiaxuan You博士论文《用图赋能深度学习》译读 摘要和感谢
  17. 如何用软件测试电源好坏,电脑电源怎么测试好坏?如何测试电脑电源好坏?
  18. MoviePy - 中文文档4-MoviePy实战案例-给MoviePy Logo做一个闪动的阴影效果
  19. 第2章 Kotlin简介 《Kotin 编程思想·实战》
  20. JOL探索synchronized锁-子路老师

热门文章

  1. 【转】Spring横切面(advice),增强(advisor),切入点(PointCut)(JAVA后端)
  2. Jenkins基础:获取Jenkins-Crumb的错误信息与对应方法
  3. python中时间模块datetime总结
  4. 华为路由器:ospf协议入门介绍
  5. 2017 Github优秀开源项目整理
  6. AD模数转换模块diy(AD9236/12位/80MSPS)
  7. Stable Diffusion 2.0来了,皮卡智能AI早已“抢先”优化升级
  8. python中求和符号怎么打_参数siz向量的python符号求和与微分
  9. android 仿微信头像裁剪,android 模仿微信头像裁剪
  10. 企业邮箱适用于哪些行业?公司邮箱都用什么?