AngularJs内存泄漏:即使被监视的元素被删除,范围$$观察者也会增加

时间:2015-03-17 00:33:13

标签: angularjs memory-leaks listeners

我的AngularJs应用程序遇到了一个大问题......

我从服务器加载HTML内容(自定义表单)。 当然,加载的HTML包含一些ng-showng-click等... 所以,我在编译之前将其编译成我的页面 这很好用。

但是,每当我$compile加载HTML时,它会在我的范围中添加更多$$watchers(当然是ng-show观察者)。

这里有一点演示/模拟:http://plnkr.co/edit/6sSazsFAugzE5XmcYkS7

我的问题:如果您点击数百次“加载一些”,$$watchers将永远不会减少并导致内存泄漏。

我尝试了remove()empty()unbind()我的元素(链接),但$$观察者数组只是不断增长,永远不会被清理。

如何解决该问题?我怎样才能清理无用的观察者或“无编译”?

感谢您的帮助!!!

2 个答案:

答案 0 :(得分:2)

由于新元素是从相同的范围和同一元素编译的,因此永远不会删除观察者。

当他们的范围被销毁(scope.$destroy())时,所有观察者都会被删除,这会在删除元素时自动发生。但是,您的元素永远不会被删除,您不断用新节点替换它。

避免这种情况的一种干净方法是避免重新编译每个新链接,而是生成动态ng-repeat列表。

否则,如果要保留代码,可以在每次编译时为元素创建一个新范围。这样,当重新编​​译它时,前一个范围将被销毁并且所有观察者都被删除。

你可以看到这里的行动。我所做的就是在编译调用中用scope替换scope.$new(),以便每次都为元素设置一个新范围:

http://plnkr.co/edit/PvUAYyb0IUoaT3dVmpGM?p=preview

PS:这是一个我从未遇到的有趣用例,它可以为Angular社区做出贡献: - )

答案 1 :(得分:0)

要正确销毁已编译元素的scope,您需要执行scope.$destroy()。我建议的解决方案是创建一个继承的范围并在之后销毁它:

    // if there was already an inherited scope, destroy it properly with $destroy()
    if (scope.inheritedScope) scope.inheritedScope.$destroy();
    // create new inherited scope every time 
    scope.inheritedScope = scope.$new(false);
    // use it in the compile
    $compile(element.contents())(scope.inheritedScope);

使用此解决方案的代码分叉: http://plnkr.co/edit/fOOHUGesbHYRiRWeLwKC

使用element.html替换HTML并且只执行scope.$new是不够的。我遇到了一个非常类似的情况,指令更复杂。一旦被替换,这个指令仍然会产生错误,所以我知道它仍然存在于内存中。