【问题标题】:How to do proper memory management using YUI to avoid leaks如何使用 YUI 进行适当的内存管理以避免泄漏
【发布时间】:2011-12-16 03:32:37
【问题描述】:

我们正在使用 YUI 的 onclick 事件,但是我们快速创建和删除圆顶节点,这会导致内存泄漏。

考虑下面这个示例代码,我们有很多 3 个嵌套的 div 很多次。顶部和底部 div 附加了 YUI onclick 事件。摆脱那些 dom 元素而不泄漏内存的正确方法是什么:

我真的没有任何想法。如您所见,我尝试实现我们自己的destroy 函数。实际上destroy 可以工作并且不会泄漏,但是速度很慢。

destroy2 函数是我们用来调试问题所在的 YUI 销毁函数的“副本”。貌似YUI的递归清理在_instances字典中找不到子节点

<!DOCTYPE html5>
<html>
    <head>
        <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script>
    </head>
    <body>
        <div id="main">hi there</div>
        <script>

        YUI().use("node", "event", function(Y) {

            window.Y = Y;

            function destroy(node) {
                (new Y.Node(node)).destroy();
                var children = node.children;
                for (var i = 0; i<children.length; i++) {
                    destroy(children[i]);
                }
            }

            function destroy2(node, recursive) {

                var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid';

                // alert(1);
                if (recursive) {

                    var all = node.all("*");
                    // alert(all);

                    Y.NodeList.each(all, function(n) {
                        instance = Y.Node._instances[n[UID]];
                        // alert(instance);
                        if (instance) {
                           destroy2(instance);
                        }
                    });

                }

                node._node = null;
                node._stateProxy = null;
                delete Y.Node._instances[node._yuid];
                // node.destroy();
            }

            var main = new Y.Node("#main");

            var divs = [];
            var iter = 0;

            Y.later(10, window, function() {

            iter ++ ;
            var i;
            for (i=0; i<divs.length; i++) {
                var d = divs[i];
                d.parentNode.removeChild(d);


                // (new Y.Node(d)).destroy(true);
                //destroy(d);
                //destroy2(new Y.Node(d), true);
                (new Y.Node(d)).destroy(true);

            }
            divs = [];

            for (i=0; i<1000; i++) {

                var d = document.createElement("div");

                var i1;
                var i2;
                d.appendChild(i1=document.createElement("div"));
                i1.appendChild(document.createTextNode('inner 1'));
                i1.appendChild(i2=document.createElement("div"));
                i2.appendChild(document.createTextNode('inner 2'));
                Y.on("click", function() {
                    alert("inner click")
                }, i2);

                // try to tell YUI to make Node elements
                Y.Node.one(d);
                Y.Node.one(i1);
                Y.Node.one(i2);

                    // new Y.Node(d);
                // new Y.Node(i1);
                // new Y.Node(i2);

                d.appendChild(document.createTextNode("this is div " + iter + " " + i));

                Y.on("click", function(){ alert("you clicked me");}, d);
                main.appendChild(d);

                //divs.push(i2);
                divs.push(d);

            }

          }, null, true);

        })
        </script>

    </body>

</html>

【问题讨论】:

    标签: javascript dom memory-leaks yui


    【解决方案1】:

    我不确定您要在这里完成什么,但包含的代码中有几件事很突出:

    1. var Y = YUI().use(…) -- use() 返回 YUI 实例。无需分配window.Y = Y;
    2. 使用Y.one(el) 代替new Y.Node(el)Y.Node.one(el)
    3. 使用event delegation而不是订阅每个内部div的点击事件
    4. 使用Y.Node.create('&lt;div&gt;&lt;div&gt;inner 1&lt;div&gt;inner2&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;')。您可能不需要每个 div 的 Node 实例
    5. 从长远来看,通过混合原始 DOM 交互和 YUI 节点,您可能会为自己创造更多的工作。如果您使用的是 YUI,请使用 YUI 的 API。

    其中最重要的一点是#3 和#4。如果您使用Node.create(或appendinsertprepend 等),则传入的标记不会为每个元素创建节点,只会为最外层的元素创建节点。如果您使用事件委托,则不需要单独的节点,这意味着您可以添加 div 结构并立即调用node.destroy()(注意不要传递true,因为内部标记没有需要清除的节点)。 node.destroy() 将清除您没有的事件侦听器,因为您正在使用事件委托,并从 _instances 字典中删除节点以在任何用户交互之前释放内存。如果用户单击其中一个节点,则该事件将被委托处理程序捕获,并且将为 e.target 元素创建一个节点。您可以在事件处理程序中调用this.destroy() 来重新清除target 的节点。

    YMMV,考虑到您的代码 sn-p 并未反映真实的用例。您也可以在 freenode 上停止#yui 以获得一些帮助或解决问题。

    【讨论】:

    • 使用事件委托可能是一种选择,但我只是想修复一些遗留代码。出于同样的原因,我们已经混合了YUI 节点和原始DOM。我们将考虑使用委托,即使在特定节点上附加事件对我们来说更容易。谢谢你的回答
    【解决方案2】:

    为了避免内存泄漏,我最终采取了以下措施:

    var destroy = function(dom) {
        var ynode = new Y.Node(dom);
        ynode.purge(true);
        ynode.destroy(true);
    }
    

    我不得不说我对 YUI 文档中关于 YUI 销毁功能的说明感到非常失望。

    以 true 作为第一个参数,它的工作方式类似于 node.purge(true)。这 destroy 方法不仅仅是分离事件订阅者。阅读 API 文档了解详细信息。 http://yuilibrary.com/yui/docs/event/

    我有点考虑 YUI 中的这个错误,因为当您递归调用 destroy 时,它不会递归调用 purge。而且看起来上面的销毁函数在 Firefox 8 上非常慢(可能还有其他版本) 和destroy 没有true

    【讨论】:

    • 如果您认为该行为是错误,则应将其提交至yuilibrary.com/projects/yui3/newticket
    • 根据票证,这个用例在 YUI 3 的更新版本中无法重现,所以显然这已经解决了。
    猜你喜欢
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 2012-10-22
    • 1970-01-01
    相关资源
    最近更新 更多