zoukankan      html  css  js  c++  java
  • Ext2.02事件机制缺陷分析,以及解决方案 (fins)

    网址:
    http://fins.javaeye.com/blog/173818

    2008-03-20更新一个临时解决方案.


    测试发现,Ext2.02在IE下无法正常释放被删除的元素(当该元素被注册了事件时)
    经过分析 发现ext事件机制中的一个bug
    (
    bug 具体描述见: http://fins.javaeye.com/blog/173218
    测试使用工具见: http://fins.javaeye.com/blog/172891
    )


    使用 el.on(eventName, fn) 为el添加事件
    调用 Ext.destroy(el) 方法移除el
    此时,如果fn为全局类型,或者是被全局对象引用, 那么会使el元素成为孤立节点,无法彻底移除.

    而如果在 Ext.destroy(el) 之前, 调用 el.un(eventName, fn) 移除添加的事件,
    那么就可以彻底移除. 但是直接使用 Ext.destroy 才是ext中描述的正确做法,
    切ext内部也都是这样使用的, 所以应该将解决问题的着手点放在 el.on 和 Ext.destroy方法上.


    Javascript代码 复制代码
    1. =============================   
    2. 销毁元素的方法(很简单)   
    3. =============================   
    4. Ext.destroy(el){   
    5.     el.removeAllListeners();   
    6.     el.removeNode();   
    7. }  

    经过测试 Ext.destroy el.removeNode 均无问题. 核心问题在 事件机制. 下面详细分析一下.


    Javascript代码 复制代码
    1. =============================   
    2. 给一个元素添加事件   
    3. =============================   
    4. Element.on(eventName, fn) {   
    5.     el=this;   
    6.     调用 EventManager.on( el,eventName, fn ){   
    7.         调用 EventManager.listener( el,eventName, fn ){   
    8.             包装   h <---- fn   
    9.             缓存  fn._handlers <---- [ [h]  ]   
    10.             调用 Ext.Event.on( el,eventName, h )  {   
    11.                 包装   wfn <---- h   
    12.                 缓存  Ext.Event._listeners  <---- [ el , eventName, h, wfn ]   
    13.                 el.addEvent( wfn )    
    14.             }   
    15.         }   
    16.     }   
    17. }   
    18. 注意:真正注册到el上的事件是wfn  


    Javascript代码 复制代码
    1. =============================   
    2. 移除一个元素的事件   
    3. =============================   
    4. Element.un(eventName, fn) {   
    5.     el=this;   
    6.     调用 EventManager.un( el,eventName, fn ){   
    7.         调用 EventManager.stopListener( el,eventName, fn ){   
    8.             取得之前缓存的 h <---- fn._handlers    
    9.             删除 fn._handlers  缓存内的相关数据    
    10.             调用 Ext.Event.un( el,eventName, h )  {   
    11.                 取得之前缓存的  wfn  <---- Ext.Event._listeners   
    12.                 el.removeEvent( wfn )    
    13.                 删除 Ext.Event._listeners 缓存内的相关数据    
    14.             }   
    15.   
    16.         }   
    17.     }   
    18. }  


    Javascript代码 复制代码
    1. =============================   
    2. 移除一个元素的所有注册的事件   
    3. =============================   
    4. Element.removeAllListeners() {   
    5.     el=this;   
    6.     调用 Ext.Event.purgeElement(el){   
    7.         取得缓存中所有的和el相关的信息   l[] <---- Ext.Event._listeners   
    8.         <循环开始 l[] >   
    9.             从 l中取得  eventName <---- l[i];   
    10.             从 l中取得  h <---- l[i];   
    11.             调用 Ext.Event.un( el,eventName, h )  {   
    12.                 取得之前缓存的  wfn  <---- Ext.Event._listeners   
    13.                 el.remove( wfn )    
    14.                 删除 Ext.Event._listeners 缓存内的相关数据    
    15.             }   
    16.         <循环结束>   
    17.   
    18.     }   
    19.   
    20. }  


    ==============================

    产生问题的原因
    执行Element.removeAllListeners时没有调用 EventManager.stopListener中的
    "删除 fn._handlers 缓存内的相关数据 "

    导致在IE下 当 fn 为全局对象 或者是被引用时, 元素无法被正确移除.

    -----------------------------------------
    如果只是简单的修改 Element.removeAllListeners

    让其 调用 Ext.Event.un 时 改成调用 EventManager.stopListener 是不行的

    因为 Element.removeAllListeners 调用 Ext.Event.un 时 ,传递的函数参数是h, 而不是最初的fn
    但是 EventManager.stopListener需要得到 最初的fn.

    -----------------------------------------
    现在的情况是 从 fn 能找到h (fn._handlers) ,但是 通过h无法找到fn
    缓存Ext.Event._listeners 中也没有存放 最初的fn.

    -----------------------------------------
    也许可以考虑在 removeAllListeners 或 purgeElement 中对 fn._handlers 进行清除,但是 拿不到 最初的fn

    -----------------------------------------
    如果之前 强制 做一个引用, 例如 h._core =fn;
    然后在 Element.removeAllListeners 加以利用 利用完之后 再清除, 似乎看起来不错
    但是我试了 ,失败 !!!!
    具体原因我也说不清



    ==================================

    我觉得 如果要解决 这个bug 确实要对ext的整个事件机制做一番大改动.(恕我直言,ext的这套事件机制真的有点太.... )

    以上是我最近研究的成果
    发上来和大家分享,如果说的不对 请务必一定马上纠正我, 以免误人子弟 谢谢大家了


    ======================================
    下面附上刚刚写出的解决方案,请大家拍砖, 我想肯定还有更好的方法.



    第一步 ========================
    EventManager.js 153行


    Javascript代码 复制代码
    1. //修改 Ext.EventManager的 私有方法 listen   
    2. //   E.on(el, ename, h);   
    3. // 改为如下 (即,多传一个最初的 fn)   
    4.   
    5.     E.on(el, ename, h , fn);  



    第二步 ========================

    ext-base.js 227行

    Javascript代码 复制代码
    1. //修改 Ext.lib.Event 的  addListener 和 removeListener 方法   
    2.   
    3. addListener: function(el, eventName, fn , ofn) {   
    4.   
    5.                 el = Ext.getDom(el);   
    6.   
    7.                 if (!el || !fn) {   
    8.                     return false;   
    9.                 }   
    10.   
    11.                 if ("unload" == eventName) {   
    12.                     unloadListeners[unloadListeners.length] =   
    13.                     [el, eventName, fn];   
    14.                     return true;   
    15.                 }   
    16.   
    17.                 // prevent unload errors with simple check   
    18.                 var wrappedFn = function(e) {   
    19.                     return typeof Ext != 'undefined' ? fn(Ext.lib.Event.getEvent(e)) : false;   
    20.                 };   
    21.        
    22.                 var li = [el, eventName, fn, wrappedFn,ofn];   
    23.                 var index = listeners.length;   
    24.                 listeners[index] = li;   
    25.   
    26.                 this.doAdd(el, eventName, wrappedFn, false);   
    27.                 return true;   
    28.   
    29.             },   
    30.   
    31.             removeListener: function(el, eventName, fn) {   
    32.                 var i, len;   
    33.   
    34.                 el = Ext.getDom(el);   
    35.   
    36.                 if(!fn) {   
    37.                     return this.purgeElement(el, false, eventName);   
    38.                 }   
    39.   
    40.   
    41.                 if ("unload" == eventName) {   
    42.   
    43.                     for (i = 0,len = unloadListeners.length; i < len; i++) {   
    44.                         var li = unloadListeners[i];   
    45.                         if (li &&   
    46.                             li[0] == el &&   
    47.                             li[1] == eventName &&   
    48.                             li[2] == fn) {   
    49.                             unloadListeners.splice(i, 1);   
    50.                             return true;   
    51.                         }   
    52.                     }   
    53.   
    54.                     return false;   
    55.                 }   
    56.   
    57.                 var cacheItem = null;   
    58.   
    59.   
    60.                 var index = arguments[3];   
    61.   
    62.                 if ("undefined" == typeof index) {   
    63.                     index = this._getCacheIndex(el, eventName, fn);   
    64.                 }   
    65.   
    66.                 if (index >= 0) {   
    67.                     cacheItem = listeners[index];   
    68.                 }   
    69.   
    70.                 if (!el || !cacheItem) {   
    71.                     return false;   
    72.                 }   
    73.   
    74.         this.doRemove(el, eventName, cacheItem[this.WFN], false);   
    75.         fn=listeners[index][4];   
    76.         if (fn){   
    77.             var id = Ext.id(el), hds = fn._handlers, hd = fn;   
    78.             if(hds){   
    79.                 for(var i = 0, len = hds.length; i < len; i++){   
    80.                     var h = hds[i];   
    81.                     if(h[0] == id && h[1] == eventName){   
    82.                         hd = h[2];   
    83.                         hds.splice(i, 1);   
    84.                         break;   
    85.                     }   
    86.                 }   
    87.             }   
    88.         }   
    89.         delete listeners[index][this.WFN];   
    90.                 delete listeners[index][this.FN];   
    91.                 listeners.splice(index, 1);   
    92.   
    93.                 return true;   
    94.   
    95.             },  
  • 相关阅读:
    读《人人都是产品经理》
    前端值得看的博客
    git 常用命令 创建查看删除分支,创建查看删除tag等
    看《如何令选择变得更加容易》
    读【失控】——众愚成智
    html5 postMessage
    下拉滚动加载更多数据
    html select用法总结
    分布式系统事务一致性解决方案
    nginx简易教程
  • 原文地址:https://www.cnblogs.com/meetrice/p/1210168.html
Copyright © 2011-2022 走看看