chrome浏览器多页签唯一关闭时自动注销
chrome浏览器多页签唯一关闭时自动注销
- 整体思路
- 多页签唯一思考
- 探索window对象name的属性
- window.name的特征
- 页签唯一实现
- 页签间共享信息思考
- 页签数量的管理实现
- 监听chrome浏览器页签关闭动作思考
- 关闭动作监听实现
- 自动注销思考
- 注销代码实现
- 测试出来的问题思考
- 页签在打开状态的心跳实现
- 总结
整体思路
技术背景:vue单页面应用
整体思路按如下问题展开:
- 多页签如何唯一?
- 页签唯一了,页签间如何共享信息?
- 页签间共享信息解决了,能否监听浏览器关闭动作?
- 浏览器关闭动作监听到了,如何自动注销?
- 自动注销实现了,是否还存在其他问题?
由上述问题可知,其中自动注销的最简单的实现,基本上就是清除浏览器记录的用户登录信息,一般都是清除相关的cookie。
多页签唯一思考
我们知道自动注销无非就是设置cookie的信息过期,实现自动清除用户的登录信息。用户在浏览器上对于同一系统只打开一个浏览器页签时,这样的实现是可以的。打开了多个页签的话,用此方式只能实现单点登出,即随意关闭一个页签导致其他页签全部注销了,这样的不符合需求和用户习惯的。我们现在的难题在于怎样识别是否只剩唯一一个页签。因此我们需要探索window对象的属性,找出可以标识页签的唯一的属性,这样就可以知道用户打开了哪些页签和关闭了哪些页签,以及最后只剩哪个页签。
探索window对象name的属性
我们通过网上查看window对象的相关属性,发现name属性可以标识页签的唯一
window.name的特征
- 每个页签都可以设置window.name,设置后的页签是独立的,无法打开相同window.name的页签;
- 可以直接赋值name,直接获取则返回当前页签名称,window.name默认是空字符串;
- 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.name和uuid实现页签的唯一,localStorage实现页签间的信息共享,监听beforeunload和unload事件实现关闭动作触发,window.setInterval定时器、监听visibilitychange事件和心跳机制实现页签打开状态的监控。
chrome浏览器多页签唯一关闭时自动注销相关推荐
- 如何解决谷歌Chrome浏览器空白页的问题
如何解决谷歌Chrome浏览器空白页的问题 谷歌Chrome浏览器突然不打开任何网页,无论是任何站点(如http://www.baidu.com), 还是Chrome浏览器的设置页面(chrome:/ ...
- fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件
fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件 参考文章: (1)fiddler无法抓取chrome浏览器请求的解决方案之关闭代理软件 (2)https://www.cnblog ...
- js 点击按钮打开浏览器新页签,兼容版
话不多说直接上代码,这是千辛万苦寻来的,帮助需要帮助的人. <html><head>点击按钮打开浏览器新页签</head><body><div o ...
- 浏览器tab页签上的title图标favicon.icon
文章目录 浏览器tab页签上的title图标favicon.icon 什么是favicon? 如何查看favicon 在线生成favicon 浏览器tab页签上的title图标如何设置 浏览器tab页 ...
- 如何解决Chrome浏览器多功能搜索框搜索时不能调用百度搜索引擎
2019独角兽企业重金招聘Python工程师标准>>> 如何解决Chrome浏览器多功能搜索框搜索时不能调用百度搜索引擎 http://www.baidu.com/s?ie=UTF- ...
- 自定义Chrome浏览器新建标签页为空白页
Chrome是我最喜欢的用的浏览器,速度快,稳定,但是有一点不喜欢,就是新建标签页中间总是有些常用网址以及搜索框,像狗皮膏药一样,既然已经有标签栏了,我觉得这些完全多余,所以就动手弄了个扩展,把标签页 ...
- chrome浏览器不支持video标签,不能自动播放。
问题:chrome浏览器不支持video标签的autoplay属性,网页刷新后不能自动播放视频. 处理: 方法一: 在video标签中加入muted="muted" 属性,但会让视 ...
- 【已解决】谷歌Chrome浏览器中如何打开或关闭网页的自动翻译功能?
对于经常查阅各种外文资料的人来说,谷 歌翻译插件 必不可少.但是今天小编要跟大家说的不是 chrome插件 ,而是chrome自带的网页翻译功能.Chrome有自带整页翻译的功能,这个我相信很 对于经 ...
- 谷歌Chrome浏览器中如何打开或关闭网页的自动翻译功能?
对于经常查阅各种外文资料的人来说,谷 歌翻译插件 必不可少.但是今天小编要跟大家说的不是 chrome插件 ,而是chrome自带的网页翻译功能.Chrome有自带整页翻译的功能,这个我相信很 对于经 ...
最新文章
- 十分钟完成Bash 脚本进阶!列举Bash经典用法及其案例
- c语言字符的内码是怎么表示,用C语言实现常见的三种中文内码转换
- php文件下载教学,php下载文件的代码示例
- mysql使用 BETWEEN AND 查询
- Linux vi的基本操作
- OpenAI新研究:通过非监督学习提升NLP任务表现
- 解决时间控件input不能选择的问题
- 裴礼文《数学分析中的典型问题与方法》P61~90
- 用axure绘制PHP工作流程图,如何用Axure绘制高质量的业务流程图?
- 使用机器学习算法打造一个简单的“微博指数”
- 【多功能改进】基于OpenCV图像采集的人脸识别网络推流及局域网无线控制系统(将图像在URL地址上输出,可做成网络摄像头,带识别框)
- 2017年保荐代表人胜任能力考试辅导教材 投资银行业务
- 一站式数字藏品收款系统开发解决方案
- 什么是联想能力?如何提高联想能力?
- TypeError: 'str' object cannot be interpreted as an integer
- 论文 | 图理论 | 2021年斯坦福大学Jiaxuan You博士论文《用图赋能深度学习》译读 摘要和感谢
- 如何用软件测试电源好坏,电脑电源怎么测试好坏?如何测试电脑电源好坏?
- MoviePy - 中文文档4-MoviePy实战案例-给MoviePy Logo做一个闪动的阴影效果
- 第2章 Kotlin简介 《Kotin 编程思想·实战》
- JOL探索synchronized锁-子路老师
热门文章
- 【转】Spring横切面(advice),增强(advisor),切入点(PointCut)(JAVA后端)
- Jenkins基础:获取Jenkins-Crumb的错误信息与对应方法
- python中时间模块datetime总结
- 华为路由器:ospf协议入门介绍
- 2017 Github优秀开源项目整理
- AD模数转换模块diy(AD9236/12位/80MSPS)
- Stable Diffusion 2.0来了,皮卡智能AI早已“抢先”优化升级
- python中求和符号怎么打_参数siz向量的python符号求和与微分
- android 仿微信头像裁剪,android 模仿微信头像裁剪
- 企业邮箱适用于哪些行业?公司邮箱都用什么?