window的addEventListener和removeEventListener方法的使用踩雷

1 背景

我们需要增加监听浏览器的 刷新/关闭tab/页面切换/页面跳转 等事件,希望在触发这些事件的时候,可以立即向服务器发送统计数据。

2 分析

【兼容性】监听页面关闭发送请求

2.1 事件的监听

beforeunload - 页面刷新/关闭
unload
pagehide
visibilitychange - 页面隐藏/可见

2.2 避免重复添加监听事件

设置全局变量_pageHideListener,每次addEventListener之前,先移除已经存在的监听事件。

否则对同一事件和监听器监听多次,会造成最后remove的时候没有remove干净,监听器事件还是会发生。

2.3 发送数据

采用 sendBeacon 异步发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。

3 代码

export class WebAdapter{
  private _pageHideListener?: (event: BeforeUnloadEvent) => void;

  private _visibilityChangeListener?: () => void;

  private _isSuccessCatchCloseEvent = false; // 判断是否已经触发了fun()函数的执行

  public addPageHiddenListener(func: () => void) {
    if (typeof window !== 'undefined' && typeof document !== 'undefined') {
      // 关键点:避免重复监听,如果已经被监听过了则需要先移除
      this.removePageHiddenListener();

      this._pageHideListener = (event) => {
        // Cancel the event as stated by the standard.
        event.preventDefault(); // 关键点
        // Chrome requires returnValue to be set.
        event.returnValue = '';
        if (!this._isSuccessCatchCloseEvent) {
          func();
          this._isSuccessCatchCloseEvent = true;
        }
      };
      this._visibilityChangeListener = () => {
        if (document.visibilityState !== 'visible') {
          func();
        } else {
          // 页面可见,需要重置_isSuccessCatchCloseEvent
          this._isSuccessCatchCloseEvent = false;
        }
      };

      window.addEventListener('beforeunload', this._pageHideListener);
      window.addEventListener('unload', this._pageHideListener);
      window.addEventListener('pagehide', this._pageHideListener);
      document.addEventListener(
        'visibilitychange',
        this._visibilityChangeListener
      );
    }
  }

  public removePageHiddenListener() {
    if (typeof window !== 'undefined' && typeof document !== 'undefined') {
      if (this._pageHideListener) {
        window.removeEventListener('beforeunload', this._pageHideListener);
        window.removeEventListener('unload', this._pageHideListener);
        window.removeEventListener('pagehide', this._pageHideListener);
      }
      if (this._visibilityChangeListener) {
        document.removeEventListener(
          'visibilitychange',
          this._visibilityChangeListener
        );
      }
    }
  }
}

参考文档

【兼容性】监听页面关闭发送请求

解决 js addEventListener重复监听

你可能感兴趣的:(前端)