编程崽

登录

一叶在编程苦海沉沦的扁舟之上,我是那只激情自射的崽

页面隐藏|激活|关闭的监听

页面隐藏|激活|关闭的监听

当开发了一个包含轮询调用接口、socket推送后实时渲染数据、持续性动画的页面时,上线后可能会出现性能问题,或者用户离开页面一段时间后,浏览器为了节省资源,会对我们的页面进行cpu、网络方面的限制,导致用户再回到页面,页面卡死或直接崩溃。

与其等浏览器把我们干死,不如我们自己只能休眠。

下面这个插件可以监听用户切换到别的页面、重新回来的事件。

可以看这个Demo,打开页面,然后切换到其他页面再回来,查看页签标题和页面中打印的信息,页面监听事件还有次数逻辑,可以看demo页面的源代码了解一下。

查看Demo

js 复制代码
// 引入
import PageWatch from './PageWatch';

// 要绑定监听的事件对象组装(下面三个字段不是必传)
let bindEvents = {
  visible: (pageWatchObj) => {}, // 回来的回调方法
  hidden: (pageWatchObj) => {}, // 离开的回调方法
  reload: (pageWatchObj) => {}, // 即将刷新页面的回调方法
}

// 监听事件绑定成功的事件(可以不用)
let bindSuccess = () => {}

// 实例化对象
new PageWatch(bindEvents, bindSuccess)

js 复制代码
// 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)
}
更新时间:2022/12/09 13:40:03