zone.js 和非 angular dom 元素事件的问题

Issue with zone.js and non angular dom element events

我遇到了 Zone.js 和非 angular DOM 元素事件的问题。

当我们将 javascript 函数分配给 HTML 标记中的任何 DOM 元素,然后以编程方式将相同的 DOM 元素重新分配给其他元素时,就会发生这种情况或相同的功能。

例如你有这个标记

<button id="button1" onclick="test1()">button 1</button>

然后在 JS 的某处你重新分配 button1 onclick 与一些其他功能

 document.getElementById('button1').onclick = test3;

在这种情况下,会进行两次函数调用。第一个是原始分配的方法,然后第二个是新分配的方法。

我创建了一个 PLUNKER 来演示此行为。

在示例中,我们在屏幕上有一个 angular 应用程序和 2 个非 angular 按钮。每个按钮最初都分配了一个功能,该功能仅将一些文本打印到控制台。

一个按钮(onclick 事件上的按钮 1)仅打印 调用的测试 1 方法。

另一个按钮(onclick 事件上的按钮 2)打印 调用的测试 2 方法。 并且还为按钮 1 的 onclick 分配一个新函数。这个新函数打印 test 3 method called.

场景-

让应用程序加载。当您看到 App works 时,请执行以下操作:-
1. 打开调试器控制台。
2.点击按钮2
3.查看控制台
4. 点击按钮 1.
5. 检查控制台。

期待。
• 单击 Button 2 时,我们应该看到调用了 test 2 方法。
• 单击 Button 1 时,我们应该看到调用了 test 3 方法。

实际
• 单击按钮 2,我们看到调用了 test 2 方法。
• 单击按钮 1 时,我们看到调用了 test 1 方法。 随后是 test 3
调用的方法。

笨蛋 link

https://plnkr.co/edit/ymKcHjWPrDiG73NfWBle?p=preview

在调试时,我看到一个方法被称为 DOM 元素事件(来自 onclick 的 test1),另一个方法(test3)被 zonejs
调用 (对于这个调试,我在本地机器的 zone.js 文件中添加了 console.log)

我看到 zonejs 注册了与屏幕上任何 DOM 元素相关的几乎所有事件。

那么,这是我面临的问题吗?或者这是预期的行为还是我哪里出错了?请帮忙提供一些信息/指点。

不是 angular 但 zonejs 导致了此行为。处理一些试错。

https://plnkr.co/edit/cnDy4utsWYbhaXIBluNw?p=preview

  1. 将内联事件侦听器移动到 window.onload
  2. 更新了 test2() 方法以删除和添加按钮 1 的侦听器

问题是区域补丁 onclick 并使用 addEventListener/removeEventListener 到 register/remove 按钮的回调。但是 onclick 注册了它自己的独立处理程序,不能用 removeEventListener 删除。所以实际上你最终得到了 click 事件的两个处理程序:

function patchProperty(obj, prop, prototype) {
    var desc = Object.getOwnPropertyDescriptor(obj, prop);
    ...
    desc.set = function (newValue) {
        ...
        var previousValue = target[_prop];
        if (previousValue) {
            // !!!!!! ------------------ it doesn't remove onclick here ---------- !!!!!!
            target.removeEventListener(eventName, previousValue); 
        }
        if (typeof newValue === 'function') {
            var wrapFn = function (event) {
              console.log(' zone wrap function called for--\n');
              console.log('targetID '+target.id);
              console.log('event is '+eventName);
                var result = newValue.apply(this, arguments);
                if (result != undefined && !result) {
                    event.preventDefault();
                }
                return result;
            };
            target[_prop] = wrapFn;

            // !!!!!! ------------------ but adds new handler here ---------- !!!!!!
            target.addEventListener(eventName, wrapFn, false);

这种情况很容易在没有区域的情况下重现:

    function onClickHandler() {
        console.log('onClickHandler');
    }

    document.addEventListener("DOMContentLoaded", function() {
        const button = document.querySelector('button');
        button.removeEventListener('click', button.onclick);
        button.addEventListener('click', ()=> {
            console.log('addEventListener')
        });
    });

   <button onclick="onClickHandler()">Hello</button>

解决方法之一是修补 zone.js 的 "desc.set" 方法来解决此问题。

function patchProperty(obj, prop, prototype) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
...
desc.set = function (newValue) {
...
var previousValue = target[_prop];
if (previousValue) {
    target.removeEventListener(eventName, previousValue);
}//Start Fix
else{ 
    if(originalDescGet){
         if (typeof target['removeAttribute'] === 'function') {
             target.removeAttribute(prop);
         }
    }   
}//End fix