【问题标题】:Make touch and wheel event listeners added by libraries as "passive"将库添加的触摸和滚轮事件侦听器设置为“被动”
【发布时间】:2022-01-04 15:12:54
【问题描述】:

我有两个项目:

  1. BootstrapjQuery
  2. materialize-cssVanilla JS

在对这两个项目进行 Lighthouse 审计时,我曾经在一个项目上收到由 materialize-css 和另一个项目上的 jQuery 引起的警告:

我说“曾经得到”,因为我确实设法为jQuery 修复了它,只需应用这个解决方法:

const opts = (ns) => ... // some code deciding if browser supports passive
$.event.special.touchmove = { setup: function(_, ns, handle) { this.addEventListener('touchmove', handle, opts(ns)) } }
$.event.special.touchstart = { setup: function(_, ns, handle) { this.addEventListener('touchstart', handle, opts(ns)) } }
$.event.special.touchend = { setup: function(_, ns, handle) { this.addEventListener('touchend', handle, opts(ns)) } }

这似乎解决了jQuery 的问题,我不再收到这样的警告,一切似乎都正常。

现在,对于materialize-css,我找到了这个包default-passive-events(来自文档):

基本上每次你都会自动设置 {passive: true } 声明一个新的事件监听器。

不幸的是,由于使用了e.preventDefault(),这个库确实使物化组件因触摸事件而中断...

是否有类似于上述jQuery 解决方法的方法来修复所有materialize-css 添加的事件侦听器?附言它不使用jQuery

【问题讨论】:

    标签: javascript jquery google-chrome materialize lighthouse


    【解决方案1】:

    首先,这只是一个警告,而不是错误。


    有没有办法,类似于上面的 jQuery 解决方法,来修复所有添加的 materialize-css 事件侦听器?附言它不使用 jQuery

    是的,实际上有三种方式:

    1. 阅读警告,而不提高性能

    只需将{ passive: false } 作为第三个参数添加到所有没有对象作为第三个参数的侦听器。这将告诉浏览器.preventDefault() 可能会在这些事件上被调用。然而,特别是在scrolltouchmovetouchstart 事件上,当浏览器知道不会阻止事件的默认行为时,性能提升是相当可观的。当标记为被动时,滚动会更加流畅,感知性能会显着提高。

    1. 通过可能破坏功能来提高性能

    { passive: true } 作为第三个参数添加到所有没有对象作为第三个参数的侦听器。这将告诉浏览器 .preventDefault() 永远不会在这些事件上被调用。您会看到性能提升,但依赖于阻止这些事件的代码将会中断。

    注意:这是 jQuery 修复和 default-passive-events 包所做的,顺便说一句。

    1. 正确方法

    正确的方法是进入您正在修复的任何库的源代码,找出可能会阻止哪些事件并为这些事件添加{ passive: false },同时为其他所有内容添加{ passive: true }
    我认为在库中找到所有阻止事件的地方并不是一项艰巨的任务。
    您可以在 fork 中执行此操作,理想情况下将其 PR-ing 回 lib 的 repo,让其他人受益,就像您从 lib 本身中受益一样。


    这里是解决方案 1。

    function patchScrollBlockingListeners() {
      let supportsPassive = false;
      const x = document.createElement("x");
      x.addEventListener("cut", () => 1, {
        get passive() { supportsPassive = true; return !!1 }
      });
      x.remove();
      if (supportsPassive) {
        const originalFn = EventTarget.prototype.addEventListener;
        EventTarget.prototype.addEventListener = function(...args) {
          if (
            ['scroll', 'touchmove', 'touchstart'].includes(args[0]) &&
            (typeof args[2] !== 'object' || args[2].passive === undefined)
          ) {
            args[2] = {
              ...(typeof args[2] === 'object' ? args[2] : {}),
              passive: false
            };
          }
          originalFn.call(this, ...args);
        }
      }
    }
    patchScrollBlockingListeners();
    

    以上代码仅“修补”scrolltouchmovetouchstart 事件(通过将它们声明为非被动)。这会使警告消失,而无需触及第三方代码。
    注意:为了使其工作,必须在加载任何引发警告的库之前运行该函数。上面的代码只修补运行后添加的事件,并没有修补已经绑定的监听器。

    注意:解决方案 2 是相同的代码,除了 passive 覆盖设置为 true

    另一个相当重要的注意事项:虽然我不能保证它对每个人都有效,但将以下内容传递给 passive 对我来说“修补”了很多库很多项目:

    passive: typeof args[2] === "boolean" ? args[2] : true
    

    它尊重之前的addEventListener 语法(其中第三个参数是passive 本身,就像boolean)并在根本没有指定时将其设置为true。但是,这会破坏未指定 passive 的事件,并且在某些情况下事件被取消,这就是我没有在上面包含它的原因。

    【讨论】:

    • 有没有办法在不修改 3rd 方代码的情况下做到这一点?目前它位于 node_modules 文件夹中
    • 是的,我将其添加到答案中。
    • 设置为 false 似乎可以消除警告,但正如您提到的,这不是修复,您知道如何确定事件是否被阻止,以便我可以确定是否可以将被动设置为 true 吗?
    • 我可以将 args[1] 转换为字符串并查看它是否包含 preventDefault? args[2] = { ...(args[2] || {}), ...(supportsPassive && { passive: !args[1].toString().includes('preventDefault') }) }
    • 如果这么简单就好了。但这是 JS。您只是获得某个函数的名称,而不是调用该函数时将运行的整个代码。做到这一点的方法是倒退的。查找对preventDefault 的所有调用,并将它们追溯到附加事件的位置。那些不可能是被动的。其他一切:被动。基本上,上面的案例3。有时这很容易,但在大型库中,这几乎是不可能的,即使对于维护人员也是如此。
    【解决方案2】:

    我创建了一个遵循@tao 的“正确方法” 的 npm 包,不仅可以消除警告,还可以提高性能。见passive-events-support

    它确实为我消除了由 MaterializeCSSjQuery 引起的警告。如果您想使用它,请务必阅读自定义部分以仅将修复应用于您需要的事件:

    import { passiveSupport } from 'passive-events-support/src/utils'
    
    passiveSupport(['touchstart', 'touchmove'])
    

    如果您不使用模块,请阅读文档了解将其导入项目的其他方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-01
      • 1970-01-01
      • 2018-02-16
      • 2020-12-09
      • 1970-01-01
      • 2011-08-16
      • 2022-01-28
      相关资源
      最近更新 更多