Passive Event Listeners (被动事件侦听器)和自定义的default-passive-events.js

236 阅读3分钟

前言

  1. Chrome 增加了新的事件捕获机制  Passive Event Listeners (被动事件侦听器),它的主要作用是: 让页面滑动更加流畅,主要用于提升移动端滑动行为的性能

   详细解释:Passive EventListeners:就是告诉前页面内的事件监听器内部是否会调用preventDefaut函数来阻止事件的默认行为,以便浏览器根据这个信息更好地做出决策来优化页面性能。当属性passive的值为true的时候,代表该监听器内部不会调用preventDefaut函数来阻止默认滑动行为,Chrome浏览器称这类型的监听器为被动(passive)监听器。目前Chrome主要利用该特性来优化页面的滑动性能,所以Passive Event Listeners特性当前仅支持mousewheel/touch相关事件。

  1. 但是新增的Passive Event Listeners (被动事件侦听器)导致新的问题

   所有的scroll事件的passive都是true。并且你想阻止你也阻止不了。document的相关事件passive默认是true,不管你是在event handle里面调用event.preventDefault()还是调用e.nativeEvent.preventDefault()都没什么用。

   浏览器里日志会有报错或者警告

   报错:

 Unable to preventDefault inside passive event listener invocation.//无法在被动事件侦听器调用中阻止Default。

   警告:

 Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive.

   //违反:没有添加被动事件监听器来阻止’touchstart’事件,请考虑添加事件管理者’passive’,以使页面更加流畅。
  1. 2的问题的解决办法,default-passive-events.js

   但是default-passive-events.js也会导致新的问题,default-passive-events有可能导致antd和element相关的,带滚动或者拖动的组件出现bug,所以我重写了default-passive-events的方法


//自定义default-passive-events.js

(function () {

  if (typeof EventTarget !== 'undefined') {

    let func = EventTarget.prototype.addEventListener;

    EventTarget.prototype.addEventListener = function (type, fn, capture) {

      this.func = func;

      if (typeof capture !== 'boolean') {

        capture = capture || {};

        capture.passive = false;

      }

      this.func(type, fn, capture);

    };

  }

})();

附录:

Chrome 51版本的passive事件监听器


// Test via a getter in the options object to see if the passive property is accessed

var supportsPassive = false;

try {

  var opts = Object.defineProperty({}, 'passive', {

    get: function() {

      supportsPassive = true;

    }

  });

  window.addEventListener("testPassive", null, opts);

  window.removeEventListener("testPassive", null, opts);

} catch (e) {}

  


// Use our detect's results. passive applied if supported, capture will be false either way.

elem.addEventListener('touchstart', fn, supportsPassive ? { passive: true } : false);

addEventListener()passive 默认值始终为 false。然而,这引入了触摸事件和滚轮事件的事件监听器在浏览器尝试滚动页面时阻塞浏览器主线程的可能性——这可能会大大降低浏览器处理页面滚动时的性能。

为了避免这一问题,大部分浏览器(Safari 和 Internet Explorer 除外)将文档级节点 WindowDocumentDocument.body 上的 wheelmousewheeltouchstarttouchmove 事件的 passive 默认值更改为 true。如此,事件监听器便不能取消事件,也不会在用户滚动页面时阻止页面呈现。

因此,当你想要覆盖这一行为并确认 passive 在所有浏览器中都被设为 false,你必须显式地将其设为 false,而不是依赖浏览器的默认设置。

不过,你无需担心基本 scroll 事件的 passive 值。因为该事件不能被取消,事件监听器也就无法阻止页面的渲染。

旧版本浏览器

在不支持 addEventListener()options 参数的旧浏览器上,尝试使用它会阻止使用 useCapture 参数而不正确使用特征检测

如果你想检测 passive 值,可以参考下面这个例子:


  


let passiveSupported = false;

  


try {

  const options = {

    get passive() {

      // 该函数会在浏览器尝试访问 passive 值时被调用。

      passiveSupported = true;

      return false;

    },

  };

  


  window.addEventListener("test", null, options);

  window.removeEventListener("test", null, options);

} catch (err) {

  passiveSupported = false;

}

  


参考:

developer.mozilla.org/zh-CN/docs/…