【问题标题】:How do I track down where event listener is getting added?如何跟踪添加事件侦听器的位置?
【发布时间】:2018-06-04 20:25:03
【问题描述】:

我有一个相当大的 javascript(带有 react/redux 但没有 jquery)代码库,用于我正在构建的 web 应用程序,我注意到当我在 UI 中反复打开和关闭某个面板时,根据 Chrome 的性能时间表,听众不断增加。

图表如下所示:

我已经让 chrome 的性能监视器运行了一两分钟,页面处于空闲状态(就在打开/关闭面板之后),希望也许听众会得到垃圾收集,但事实并非如此。在此过程中,我已切换到其他选项卡,也希望在选项卡处于后台时,侦听器会得到垃圾收集,但不幸的是他们没有。

因此,我怀疑有些听众正在注册,但从未取消注册。

这让我想到了两个主要问题:

  1. 我的假设是听众正在增加而永远不会 unbound 似乎是明智的,或者我可以做更多的事情来确认 这种怀疑?
  2. 假设我的怀疑是正确的,我怎样才能最好地去 关于追踪事件监听器所在的代码 被添加?我已经尝试过以下方法:
    • 查看了负责打开相关面板的代码,查看它在何处添加了任何侦听器,并注释掉这些部分以查看性能图表是否有任何变化。没有变化。
    • 像这样覆盖 addEventListener 原型:

 

var f = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, fn, capture) {
    this.f = f;
    this.f(type, fn, capture);
    console.trace("Added event listener on" + type);
}

即使在这样做之后,然后注释掉导致执行此 console.trace 的所有代码部分(参见 #1),以便在面板打开/关闭时不再打印 console.trace,我注意到同样的性能图中的侦听器增加。其他原因导致听众增加。我知道还有其他方法可以添加侦听器,但我不清楚如何拦截所有这些可能性或使它们以这样的方式登录到 Chrome 的调试器中,以便我可以知道哪些代码负责添加它们.

编辑: - 在cmets中cowbert的建议下,我看了一下这个页面: https://developers.google.com/web/tools/chrome-devtools/console/events

然后我做了以下函数:

function printListenerCount() {
    var eles = document.getElementsByTagName("*");
    var numListeners = 0;
    for (idx in eles) { let listeners = getEventListeners(eles[idx]);
        for(eIdx in listeners)
        {
            numListeners += listeners[eIdx].length;
        }
        console.log("ele", eles[idx], "listeners", getEventListeners(eles[idx]));
    }
    console.log("numListeners", numListeners)
}

我在多次打开/关闭面板后执行此功能,但不幸的是“numListeners”数字没有改变。 如果 numListeners 数字发生了变化,我将能够在打开/关闭面板之前/之后比较结果以发现哪个元素 已经注册了额外的事件监听器,但不幸的是 numListeners 没有改变。

https://developers.google.com/web/tools/chrome-devtools/console/events 上也描述了一个 monitorEvents() API,但是函数 call 要求您指定要监视的 DOM 元素。在这种情况下,我不确定哪个 DOM 元素有额外的 听众,所以我不确定 monitorEvents() 调用将如何真正帮助我。我可以将它附加到所有 DOM 元素,类似于我的方式 写了上面的 printListenerCount 函数,但我想我会遇到与 printListenerCount() 类似的问题—— 无论出于何种原因,它都没有考虑到有问题的听众。

其他说明: 这是一个有点复杂的基于 reactjs(preact,技术上)的应用程序。像大多数基于 reactjs 的应用程序一样,组件可以即时安装/卸载(插入到 DOM 中或从 DOM 中删除)。我发现这使得跟踪像这样的“杂散事件处理程序注册”有点棘手。所以我真正希望的是一些关于如何在诸如此类的大型/复杂项目中追踪“杂散事件处理程序”的一般调试建议。作为 C 程序员,我会打开 gdb 并在所有可能导致性能图中“侦听器”数量增加的事物上设置断点。我不确定javascript世界中是否有类似的东西,即使有,我也不确定该怎么做。任何建议将不胜感激!

【问题讨论】:

  • 您是否尝试过查看 chrome 中的 monitorEvents() API? developers.google.com/web/tools/chrome-devtools/console/events
  • 谢谢,我看了一下,并在我的 OP 中添加了注释。
  • 您可以尝试在代码中搜索 addEventListener 的所有实例并在其上放置断点。与原型替换后得到的应该没有太大不同,除非有人在你做之前做了同样的事情......

标签: javascript


【解决方案1】:

感谢大家的 cmets。我最终弄明白了。

来自我的 OP:

  1. 我关于听众不断增加且永远不会解绑的假设似乎是明智的,还是我可以做更多的事情来证实这种怀疑?

事实证明,这个问题的答案是:假设是不明智的。听众根本没有机会收集垃圾。这可能需要比您想象的更多的时间。

我是这样想的: 我没有意识到在记录性能时间线时,可以通过单击“性能”选项卡中的垃圾桶图标来强制进行垃圾收集(用于开始时间线记录的相同选项卡)。通过在反复关闭/打开 UI 面板后单击此图标,额外的侦听器完全消失了。该图现在看起来像这样,下降是我单击垃圾桶图标的时刻:

显然,像我在 OP 中提到的那样,将选项卡置于后台并等待几分钟根本不足以让垃圾收集自行发生;这需要更多的时间。

当我写 OP 时,我并不知道使用垃圾桶图标手动收集垃圾的能力......我强烈建议在进行任何野鹅追逐之前使用它,以寻找最初看起来像表演的东西问题。

【讨论】:

  • 这里的“性能问题”不是您误解/误解了图表吗?也就是说,没有任何实际测量的性能问题,您认为有一个问题并最终追逐您的尾巴一段时间。当然,这可能发生在任何人身上,但解决方案不是“在你的工具 UI 中找到可以消除想象中的问题的按钮”,而是“尝试更好地了解你的运行时环境以及你的工具告诉你什么'。我不确定您在这里作为答案提出的结论是否正确。
  • 我想知道是不是因为当你“删除”一个对象时,默认情况下 preact/仍然缓存它以防你想再次重绘它,并且它的缓存仍然绑定它的处理程序。跨度>
  • 是的,在我的 OP 中,我发布了以下问题“我的假设听众正在被添加并且永远不会解除绑定似乎是明智的,还是我可以做更多的事情来证实这种怀疑?”。事实证明,该问题的答案是“您怀疑存在真正的性能问题是不正确的,这就是您如何更好地做出该决定......”。当我说“假设不合理”时,我认为我在回答中已经说得很清楚了。如果你觉得不清楚,你能指出原因吗?谢谢。
  • 我想我看到了它可能令人困惑的地方,在最后一句话中。我将答案中的最后一句话从:“我强烈建议在尝试缩小像这样的任何潜在性能问题时使用它。”致:“我强烈建议在进行任何疯狂追逐之前使用它,以寻找乍一看可能看起来像性能问题的东西。”如果您仍然不清楚,请告诉我。
  • 我的意思是,是的,我误解了图表。但我的部分操作本质上是“我是否有可能误解了这个图表?如果是这样,我怎么能确定?”。因为这实际上是我最初问题的一部分,所以我认为这是一个完全可以接受的答案,具体解释图表是如何被误解的,以及如何防止将来出现这种特殊误解的方法。
猜你喜欢
  • 2016-10-31
  • 2017-09-24
  • 2016-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多