페이지에서 총 시계 수를 계산하는 방법은 무엇입니까?


144

JavaScript에서 전체 페이지의 각도 시계 수를 계산하는 방법이 있습니까?

우리는 Batarang을 사용 하지만 항상 우리의 요구에 맞는 것은 아닙니다. 우리의 응용 프로그램은 크며 자동 테스트를 사용하여 시계 수가 너무 많이 올라가는 지 확인하고 싶습니다.

컨트롤러별로 시계를 계산하는 것도 유용합니다.

편집 : 여기 내 시도입니다. 그것은 클래스 ng-scope로 모든 시계를 계산합니다.

(function () {
    var elts = document.getElementsByClassName('ng-scope');
    var watches = [];
    var visited_ids = {};
    for (var i=0; i < elts.length; i++) {
       var scope = angular.element(elts[i]).scope();
       if (scope.$id in visited_ids) 
         continue;
       visited_ids[scope.$id] = true;
       watches.push.apply(watches, scope.$$watchers);
    }
    return watches.length;
})();

컨트롤러 당 쉽습니다. 모든 사람 $scope은 해당 컨트롤러에 감시자 수와 함께 $$ watchers 배열을 가지고 있습니다. 그러나 전체 앱에서 모든 시계를 볼 수있는 방법이 없다고 생각합니다.
Jesus Rodriguez

답변:


220

( 을 넣은 곳이나 장소 를 바꿔야 body할 수도 html있습니다 ng-app)

(function () { 
    var root = angular.element(document.getElementsByTagName('body'));

    var watchers = [];

    var f = function (element) {
        angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) { 
            if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
                angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
                    watchers.push(watcher);
                });
            }
        });

        angular.forEach(element.children(), function (childElement) {
            f(angular.element(childElement));
        });
    };

    f(root);

    // Remove duplicate watchers
    var watchersWithoutDuplicates = [];
    angular.forEach(watchers, function(item) {
        if(watchersWithoutDuplicates.indexOf(item) < 0) {
             watchersWithoutDuplicates.push(item);
        }
    });

    console.log(watchersWithoutDuplicates.length);
})();
  • 이 답변에 지적한 것에 대한 erilem 덕분에 $isolateScope검색 이 누락 되었고 감시자가 잠재적으로 자신의 답변 / 댓글에 복제되고 있습니다.

  • 'body'변경이 필요할 수 있음 을 지적한 Ben2307에게 감사합니다 .


실물

클래스가 아닌 HTML 요소의 데이터 속성을 확인한 것을 제외하고는 동일한 작업을 수행했습니다. 나는 너를 여기에서 달렸다.

http://fluid.ie/

83을 얻었습니다. 나는 내 것을 달렸고 121을 얻었습니다.

(function () { 
    var root = $(document.getElementsByTagName('body'));
    var watchers = [];

    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            angular.forEach(element.data().$scope.$$watchers, function (watcher) {
                watchers.push(watcher);
            });
        }

        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };

    f(root);

    console.log(watchers.length);
})();

나는 또한 이것을 내 안에 넣었다.

for (var i = 0; i < watchers.length; i++) {
    for (var j = 0; j < watchers.length; j++) {
        if (i !== j && watchers[i] === watchers[j]) {
            console.log('here');
        }
    }
}

그리고 아무것도 인쇄되지 않았으므로 광산이 더 나을 것입니다 (더 많은 시계를 찾았다는 점). 그러나 광산이 솔루션 세트의 적절한 하위 집합이 아님을 알 수있는 친밀한 각도 지식이 부족합니다.


2
나는 비슷한 것을하려고 노력 했으며이 발췌 문장이 매우 유용하다는 것을 알았습니다. 내가 분명히 가치가 있다고 생각하는 것 중 하나는 'root'가 'ng-app'속성을 가진 모든 요소로 설정되어야한다는 것입니다. $ rootScope가 유지되는 곳입니다. 내 응용 프로그램에서는 'html'태그에 있습니다. 스크립트를 실행하면 내 앱의 $ rootScope에서 $ watchers가 누락되었습니다.
aamiri

위의 코드에서 한 가지 문제를 발견했습니다. 자식 요소에 자체 범위가 있지만 이제 감시자는 $ scope가 아닙니다. $$ watchers는 부모 범위의 감시자입니까? 나는 밀어 넣기 전에 다음과 같은 것을 추가해야한다고 생각한다. (나는 lodash를 사용하고있다) : if (! _. contains (watchers, watcher)) {watchers.push (watcher); }
Ben2307

2
DOM 요소에 연결된 모든 감시자를 가져옵니다. DOM 요소를 제거하면 관련 $scope$$watcher자동 정리가 수행됩니까, 아니면 성능이 저하됩니까?
SimplGy

새로운 일회성 바인딩 기능을 고려합니까? 카운트가 이런 종류의 바인딩을 건너 뛰나요?
systempuntoout

10
단지 머리 만 숙입니다 : $compileProvider.debugInfoEnabled(false);프로젝트에서 사용하는 경우이 스 니펫은 0 명의 감시자를 계산합니다.
Michael Klöpzig


12

다음은 범위 구조 검사를 기반으로 한 해키 솔루션입니다. 작동하는 것은 "보인다". 이것이 얼마나 정확한지 잘 모르겠으며 내부 API에 따라 다릅니다. angularjs 1.0.5를 사용하고 있습니다.

    $rootScope.countWatchers = function () {
        var q = [$rootScope], watchers = 0, scope;
        while (q.length > 0) {
            scope = q.pop();
            if (scope.$$watchers) {
                watchers += scope.$$watchers.length;
            }
            if (scope.$$childHead) {
                q.push(scope.$$childHead);
            }
            if (scope.$$nextSibling) {
                q.push(scope.$$nextSibling);
            }
        }
        window.console.log(watchers);
    };

이것은 내 원래 솔루션과 비슷합니다 (편집 기록 참조). 범위 계층 구조를 걷는 것이 격리 범위를 놓칠 것이라고 생각하기 때문에 다른 접근법으로 옮겼습니다.
ty.

3
내가 가진 격리 된 범위를 만들 경우 $rootScope.$new(true)또는 $scope.$new(true), 어디 $scope컨트롤러의 다음 계층 구조를 걷고있다 여전히 그 범위를 찾습니다. 스코프가 계층 구조에 없다는 대신 프로토 타입이 연결되어 있지 않다는 것을 의미한다고 생각합니다.
Ian Wilson

예, 모든 범위는 $ rootScope에서 내려 오며 격리 된 범위에서는 상속 만 "절연"됩니다. 격리 된 범위는 지시문에 자주 사용됩니다. 여기서 부모의 앱 변수가 간섭하는 것을 원하지 않습니다.
markmarijnissen

작성한 범위를 감지하려고하지만 정리하지 않는 경우이 방법이 우수합니다. 필자의 경우 DOM을 크롤링하면 항상 동일한 수의 범위가 표시되지만 DOM에 연결되지 않은 범위는 섀도우 랜드에서 증가합니다.
SimplGy


9

최근에 내 응용 프로그램에서 많은 수의 감시자와 씨름하고 있었기 때문에 ng-stats - https : //github.com/kentcdodds/ng-stats 라는 훌륭한 라이브러리를 발견했습니다 . 최소 설정으로 현재 페이지에있는 감시자 수 + 다이제스트주기 길이를 제공합니다. 작은 실시간 그래프를 투사 할 수도 있습니다.


8

Jared의 답변과 같은 단어의 사소한 개선 .

(function () {
    var root = $(document.getElementsByTagName('body'));
    var watchers = 0;

    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            watchers += (element.data().$scope.$$watchers || []).length;
        }

        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };

    f(root);

    return watchers;
})();

2
jQuery 선택기를 사용하지 않기 때문에 $ () 대신 angular.element ()를 사용할 수 있습니다.
beardedlinuxgeek

8

AngularJS 1.3.2에서는 countWatchersngMock 모듈에 메소드가 추가되었습니다.

/ **
 * @ngdoc 방법
 * @name $ rootScope.Scope # $ countWatchers
 * @ 모듈 ngMock
 * @ 설명
 * 현재 범위의 직접 및 간접 하위 범위의 모든 감시자를 계산합니다.
 *
 * 현재 범위의 감시자도 카운트에 포함되며 모든 감시자도
 * 하위 범위를 분리합니다.
 *
 * @returns {number} 총 감시자 수입니다.
 * /

  함수 countWatchers () 
   {
   var root = angular.element (document) .injector (). get ( '$ rootScope');
   var count = root. $$ watchers? root. $$ watchers.length : 0; // 현재 범위를 포함
   var pendingChildHeads = [root. $$ childHead];
   var currentScope;

   while (pendingChildHeads.length) 
    {
    currentScope = pendingChildHeads.shift ();

    while (currentScope) 
      {
      count + = currentScope. $$ watchers? currentScope. $$ watchers.length : 0;
      pendingChildHeads.push (currentScope. $$ childHead);
      currentScope = currentScope. $$ nextSibling;
      }
    }

   반품 횟수;
   }

참고 문헌


1
감사! 나는 변경 angular.element(document)angular.element('[ng-app]')와 경고와 함께 북마크에 넣어 :alert('found ' + countWatchers() + ' watchers');
정의되지 않은

4

$digest함수 자체 에서 직접 아래 코드를 가져 왔습니다 . 물론 document.body맨 아래에서 애플리케이션 요소 선택기 ( ) 를 업데이트해야 할 수도 있습니다 .

(function ($rootScope) {
    var watchers, length, target, next, count = 0;

    var current = target = $rootScope;

    do {
        if ((watchers = current.$$watchers)) {
            count += watchers.length;
        }

        if (!(next = (current.$$childHead ||
                (current !== target && current.$$nextSibling)))) {
            while (current !== target && !(next = current.$$nextSibling)) {
                current = current.$parent;
            }
        }
    } while ((current = next));

    return count;
})(angular.element(document.body).injector().get('$rootScope'));


1

이것이 내가 사용하는 기능입니다.

/**
 * @fileoverview This script provides a window.countWatchers function that
 * the number of Angular watchers in the page.
 *
 * You can do `countWatchers()` in a console to know the current number of
 * watchers.
 *
 * To display the number of watchers every 5 seconds in the console:
 *
 * setInterval(function(){console.log(countWatchers())}, 5000);
 */
(function () {

  var root = angular.element(document.getElementsByTagName('body'));

  var countWatchers_ = function(element, scopes, count) {
    var scope;
    scope = element.data().$scope;
    if (scope && !(scope.$id in scopes)) {
      scopes[scope.$id] = true;
      if (scope.$$watchers) {
        count += scope.$$watchers.length;
      }
    }
    scope = element.data().$isolateScope;
    if (scope && !(scope.$id in scopes)) {
      scopes[scope.$id] = true;
      if (scope.$$watchers) {
        count += scope.$$watchers.length;
      }
    }
    angular.forEach(element.children(), function (child) {
      count = countWatchers_(angular.element(child), scopes, count);
    });
    return count;
  };

  window.countWatchers = function() {
    return countWatchers_(root, {}, 0);
  };

})();

이 함수는 해시를 사용하여 동일한 범위를 여러 번 계산하지 않습니다.


element.data()때로는 정의되지 않거나 무언가가 될 수 있다고 생각 합니다 (적어도 1.0.5 응용 프로그램에서 이것을 도청하고 호출하려고 할 때 오류가 발생했습니다 countWatchers). 참고로
Jared

1

Lars Eidnes의 블로그 ( http://larseidnes.com/2014/11/05/angularjs-the-bad-parts/) 에 게시 된 재귀 함수 는 총 수의 감시자를 수집합니다. 여기에 게시 된 기능과 블로그에 게시 된 기능을 사용하여 결과를 비교하여 약간 더 높은 수를 생성했습니다. 어느 것이 더 정확한지 알 수 없습니다. 교차 참조로 여기에 추가되었습니다.

function getScopes(root) {
    var scopes = [];
    function traverse(scope) {
        scopes.push(scope);
        if (scope.$$nextSibling)
            traverse(scope.$$nextSibling);
        if (scope.$$childHead)
            traverse(scope.$$childHead);
    }
    traverse(root);
    return scopes;
}
var rootScope = angular.element(document.querySelectorAll("[ng-app]")).scope();
var scopes = getScopes(rootScope);
var watcherLists = scopes.map(function(s) { return s.$$watchers; });
_.uniq(_.flatten(watcherLists)).length;

참고 : Angular 앱의 경우 "ng-app"를 "data-ng-app"로 변경해야 할 수 있습니다.


1

Plantian의 답변이 더 빠릅니다 : https://stackoverflow.com/a/18539624/258482

여기 내가 손으로 쓴 기능이 있습니다. 재귀 함수 사용에 대해서는 생각하지 않았지만 이것이 내가 대신 한 것입니다. 좀 더 나을지도 모르겠다.

var logScope; //put this somewhere in a global piece of code

그런 다음 이것을 가장 높은 컨트롤러 안에 넣으십시오 (글로벌 컨트롤러를 사용하는 경우).

$scope.$on('logScope', function () { 
    var target = $scope.$parent, current = target, next;
    var count = 0;
    var count1 = 0;
    var checks = {};
    while(count1 < 10000){ //to prevent infinite loops, just in case
        count1++;
        if(current.$$watchers)
            count += current.$$watchers.length;

        //This if...else is also to prevent infinite loops. 
        //The while loop could be set to true.
        if(!checks[current.$id]) checks[current.$id] = true;
        else { console.error('bad', current.$id, current); break; }
        if(current.$$childHead) 
            current = current.$$childHead;
        else if(current.$$nextSibling)
            current = current.$$nextSibling;
        else if(current.$parent) {
            while(!current.$$nextSibling && current.$parent) current = current.$parent;
            if(current.$$nextSibling) current = current.$$nextSibling;
            else break;
        } else break;
    }
    //sort of by accident, count1 contains the number of scopes.
    console.log('watchers', count, count1);
    console.log('globalCtrl', $scope); 
   });

logScope = function () {
    $scope.$broadcast('logScope');
};

그리고 마지막으로 북마크입니다.

javascript:logScope();

0

이 질문에 조금 늦었지만 이것을 사용합니다.

angular.element(document.querySelector('[data-ng-app]')).scope().$$watchersCount

올바른 querySelector를 사용하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.