AngularJS指令使用$http会导致内存泄漏 - .resolve似乎不起作用?

3

我似乎遇到了一个问题,而且我认为我看不到解决方案,也许有人可以帮忙... 我有一个 AngularJS 指令,它发出一个 $http 请求,返回 HTML 并在有内容时将其注入到视图中 - 我知道这并不好,应该用 JSON 替换返回的 HTML 并使用模板,但我无法控制从 $http 请求返回的内容。指令需要一些参数/属性来获取正确的内容,所有的东西看起来都是工作正常的。该指令通常在我的视图中出现7次,一次在我的导航栏中,我使用 ng-include 将其添加到我的索引中,在我的页脚中三次出现,我使用ng-include 将其添加到我的 index.html 中,然后该指令出现在我的 ng-view 标签内,该标记可以在每个视图中出现 0 到 3 次。这是我 HTML 中的指令:

<div data-cms-inject data-page-name="homePageContent" data-slot-name="slot1"></div>

以下是JS代码示例:

.directive('cmsInject', ['$rootScope', '$q', '$http', function ($rootScope, $q, $http) {
        'use strict';
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {

                var canceller = $q.defer(),
                    cmsPromise = $http({
                    url : 'url/to/feed',
                    timeout: canceller.promise,
                    method: 'POST',
                    data: [{
                    pageName: attrs.pageName,
                    slotName: attrs.slotName
                    }]
                });

                cmsPromise.success(function(data) {

                    var resp = data[0];

                    if(resp.replace(/\s{1,}/,'').replace(/\r?\n|\r/g,'') !== 'null') {
                        element.html(resp);
                    }
                    console.log('new request success');
                    $rootScope.$broadcast('cmsLoaded');
                }).error(function(data) {
                    console.log('new request error', data);

                }) ;

                $rootScope.$on('$locationChangeStart', function () {
                    console.log('$locationChangeStart');
                    canceller.resolve('locationChange');
                });

            }
        };
    }])

现在看起来一切都很正常,但是当我快速导航浏览我的应用程序时,我注意到应用程序似乎变慢了,然后崩溃了浏览器,因此我们有一个内存泄漏。起初我认为这是由于挂起的 $http 调用引起的,但是当我尝试使用超时和"$locationChangeStart"事件来解决这些问题时,问题仍然存在。然后我尝试写入控制台以查看发生了什么......这是输出的结果。

这是当我加载应用程序时,我们有 7 个指令实例,其中 3 个位于 ng-view 中,4 个位于 ng-include 中。

7 new request success

然后我切换到一个没有指令的新视图。

7 $locationChangeStart

我随后返回到原始视图。
7 $locationChangeStart
3 new request success

我再次切换到一个没有指令的新视图

10 $locationChangeStart

我返回到原始视图。
10 $locationChangeStart
3 new request success

我再次切换到一个没有指令的新视图

13 $locationChangeStart

最后回到原始视图

13 $locationChangeStart
3  new request success

当$locationChangeStart事件被调用的次数增加时,某些指令似乎被遗留在了内存 / DOM中,比指令出现在页面中的次数更多。ng-include + ng-view,有人能看出我做错了什么吗?这里似乎有一些奇怪的地方。

3个回答

4
事件处理程序数量的增加可以解释为:
$rootScope.$on('$locationChangeStart'

你将事件处理程序附加到$rootScope,当然,这不会在指令的生命周期结束时消失。事件处理程序应该附加到scope


1
你的指令在DOM和js对象之间创建循环引用。尽量避免将事件绑定到rootScope并在指令链接函数中使用像$http这样的服务。更好的解决方案是将此逻辑放置在模块运行函数中。
类似这样的内容:
.run(['$rootScope', 'myService',  function($rootScope, myService){

    $rootScope.$on('$locationChangeStart', function () {
       myService.doSomethng().then(function(data){
            var resp = data[0];
            if(resp.replace(/\s{1,}/,'').replace(/\r?\n|\r/g,'') !== 'null') {
               $rootScope.$broadcast('cmsLoaded', data);
            }
       });
    });

}])
.directive('cmsInject', ['$rootScope', '$q', '$http', function ($rootScope, $q, $http) {
        'use strict';
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
               scope.$on('cmsLoaded', function(e, args) {
                    element.html(args);                    
               })
            }
        }
     }])

1
将您的http调用移至某个服务并在指令中使用该服务。监听指令的“$destory”事件,如果可能,进行一些清理工作。

你能详细说明一下当监听指令的'$destory'事件时我需要做什么吗?谢谢。 - Mike Sav
如果您的代码中存在使用jQuery附加的事件处理程序,您可以将其删除。您可以清除超时。如果您在代码中使用观察器,也可以将其移除。 - Jagadish Dharanikota
我已经通过删除所有对$rootScope的引用来解决了它,但是感谢关于$destroy的建议! - Mike Sav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接