登录
当开发了一个包含轮询调用接口、socket推送后实时渲染数据、持续性动画的页面时,上线后可能会出现性能问题,或者用户离开页面一段时间后,浏览器为了节省资源,会对我们的页面进行cpu、网络方面的限制,导致用户再回到页面,页面卡死或直接崩溃。
与其等浏览器把我们干死,不如我们自己只能休眠。
下面这个插件可以监听用户切换到别的页面、重新回来的事件。
可以看这个Demo,打开页面,然后切换到其他页面再回来,查看页签标题和页面中打印的信息,页面监听事件还有次数逻辑,可以看demo页面的源代码了解一下。
// 引入
import PageWatch from './PageWatch';
// 要绑定监听的事件对象组装(下面三个字段不是必传)
let bindEvents = {
visible: (pageWatchObj) => {}, // 回来的回调方法
hidden: (pageWatchObj) => {}, // 离开的回调方法
reload: (pageWatchObj) => {}, // 即将刷新页面的回调方法
}
// 监听事件绑定成功的事件(可以不用)
let bindSuccess = () => {}
// 实例化对象
new PageWatch(bindEvents, bindSuccess)
// PageWatch.js
// 绑定激活和离开的方法
export default class PageWatch {
success = undefined // 切换一次的回调函数
status = undefined // 当前的状态
visibleNum = undefined // 离开的次数
hiddenNum = undefined // 归来的次数
_prefix = undefined // 前缀
_eventName = undefined // 浏览器监听事件的事件名,可能需要加前缀
_attributeName = undefined // 浏览器获取当前状态的事件名,可能需要加前缀
_nowEvent = undefined // 当前绑定的归来或离开的事件们
constructor(objData, success) {
this.status = undefined
this.visibleNum = undefined
this.hiddenNum = undefined
this._prefix = undefined
this._eventName = undefined
this._attributeName = undefined
this._nowEvent = undefined
this.init(objData)
this.success = success
}
init(eventObj) {
this.status = undefined
this.visibleNum = 0
this.hiddenNum = 0
this._nowEvent = {}
// 判断浏览器是否支持
this._prefix = null // 前缀
if ('hidden' in document) this._prefix = '';
else {
let prefixes = ['webkit', 'moz', 'ms', 'o'];
for (let i = 0; i < prefixes.length; i++) {
if ((prefixes[i] + 'Hidden') in document)
this._prefix = prefixes[i]
break
}
}
let obj = { // 记录浏览器支持情况
reload: true,
visible: true,
hidden: true
}
if (this._prefix === null) {
obj.visible = false
obj.hidden = false
}
this._eventName = (this._prefix ? this._prefix + 'Hidden' : 'hidden').replace(/[H|h]idden/, '') + 'visibilitychange';
this._attributeName = this._prefix ? this._prefix + 'VisibilityState' : 'visibilityState';
// 如果初始化时传入了事件对象
if (typeof (eventObj) === 'object' && Array.isArray(eventObj) === false) this.add(eventObj)
this.eventChange.updataEvent(obj) // 监听事件,一直监听
this.success && this.success(obj)
}
// 移除事件监听
removeEvent() {
this.eventChange.removeEvent()
}
add(eventObj) {
if (!(typeof (eventObj) === 'object' && Array.isArray(eventObj) === false)) return new Error(eventObj, ' 不是对象')
// 更新要绑定的事件
for (let key in eventObj) {
if (key === 'visible' || key === 'hidden' || key === 'reload') {
this._nowEvent[key] = eventObj[key]
}
}
}
// this.remove 需要的字符串判断和处理函数
_remove_handle(string) {
if (string === 'visible' || string === 'hidden' || string === 'reload') {
delete this._nowEvent[string]
}
}
remove(eventNames) {
if (typeof (eventNames) !== 'string' && Array.isArray(eventNames) !== true) return new Error(eventNames, ' 不是 string 且不是 array')
// 更新要绑定的事件
if (typeof (eventNames) === 'string') this._remove_handle(eventNames)
else if (Array.isArray(eventNames)) {
for (let i = 0; i < eventNames.length; i++) {
this._remove_handle(eventNames[i])
}
}
}
// 内部更新事件
eventChange = ((__this) => {
// console.log(_this)
const _this = __this || {}
const inside_event = {
hidden() {
this.hiddenNum++ // 更新离开的次数
this.status = 'hidden'
_this._nowEvent.hidden && _this._nowEvent.hidden(_this)
},
visible() {
_this.visibleNum++ // 更新归来的次数
_this.status = 'visible'
_this._nowEvent.visible && _this._nowEvent.visible(_this)
},
reload: function (e) {
if (!_this._nowEvent.reload) return false
let needTip = {} // 用于判断是否需要浏览器弹窗提醒
_this._nowEvent.reload && _this._nowEvent.reload(_this, needTip)
// 判断是否需要浏览器弹窗提醒
if (typeof(needTip) === 'object') {
let bool = false
for (let key in needTip) {
if (needTip[key]) {
bool = true
break
}
}
// 需要阻止
if (bool) {
// eslint-disable-next-line
let confirmationMessage = "\o/";
(e || window.event).returnValue = confirmationMessage; // Gecko and Trident
return confirmationMessage;
}
}
}
}
// 监听到页面前后台切换发生了变化
let insideFn_show = () => {
inside_event[document[_this._attributeName]]()
}
// 监听到页面更新了
let insideFn_reload = () => {
inside_event['reload']()
}
return {
updataEvent(obj) {
obj.visible && document.addEventListener(_this._eventName, insideFn_show, false)
window.addEventListener('beforeunload', insideFn_reload, false)
},
removeEvent() {
document.removeEventListener(_this._eventName, insideFn_show, false)
window.removeEventListener('beforeunload', insideFn_reload, false)
}
}
})(this)
}