Wie man die Gesamtzahl der Uhren auf einer Seite zählt?

Gibt es eine Möglichkeit, in JavaScript die Anzahl der angularuhren auf der gesamten Seite zu zählen?

Wir benutzen Batarang , aber es entspricht nicht immer unseren Bedürfnissen. Unsere Anwendung ist groß und wir sind daran interessiert, automatisierte Tests zu verwenden, um zu überprüfen, ob die Anzahl der Uhrzeiten zu hoch ist.

Es wäre auch nützlich, Uhren auf einer Controller-Basis zu zählen.

Edit : Hier ist mein Versuch. Es zählt Uhren in allem mit class 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; })(); 

   

(Möglicherweise müssen Sie den body in html ändern oder wo auch immer Sie Ihre 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); })(); 
  • Dank erilem, der auf diese Antwort hingewiesen hat, fehlte die $isolateScope Suche und die Beobachter wurden möglicherweise in ihrer Antwort / ihrem Kommentar dupliziert.

  • Danke an Ben2307 für den Hinweis, dass der 'body' möglicherweise geändert werden muss.


Original

Ich habe das Gleiche gemacht, außer dass ich das Datenattribut des HTML-Elements und nicht seine class überprüft habe. Ich habe deine hier hingelegt:

http://fluid.ie/

Und bekam 83. Ich rannte meins und bekam 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); })(); 

Ich habe das auch in meinen geschrieben:

 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'); } } } 

Und nichts wird ausgedruckt, also vermute ich, dass meins besser ist (weil es mehr Uhren gefunden hat) - aber mir fehlt intimes kantiges Wissen, um sicher zu wissen, dass meins keine richtige Teilmenge des Lösungssatzes ist.

Ich denke die genannten Ansätze sind ungenau, da sie Beobachter im selben scope doppelt zählen. Hier ist meine Version eines Bookmarklets:

https://gist.github.com/DTFagus/3966db108a578f2eb00d

Es zeigt auch einige weitere Details zur Analyse von Beobachtern.

Hier ist eine Hacky-Lösung, die ich basierend auf der Inspektion der Scope-Strukturen zusammengestellt habe. Es scheint “zu funktionieren”. Ich bin mir nicht sicher, wie genau das ist und es hängt definitiv von einer internen API ab. Ich benutze 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); }; 

Es gibt ein neues Chrome-Plugin, das automatisch die aktuellen Gesamtbeobachter und die letzte Änderung (+/-) zu jeder Zeit in Ihrer App anzeigt … es ist einfach großartig.

https://chrome.google.com/webstore/detail/angular-watchers/nlmjblobloedpmkmmckeehnbfalnjnjk

Da ich mich in letzter Zeit mit einer hohen Anzahl von Beobachtern in meiner Anwendung herumgeschlagen habe, habe ich eine große Bibliothek namens ng-stats gefundenhttps://github.com/kentcdodds/ng-stats . Es hat minimale Einstellungen und gibt Ihnen die Anzahl der Beobachter auf der aktuellen Seite + Digest-Zyklus Länge. Es könnte auch ein kleines Echtzeitdiagramm projizieren.

Kleine Verbesserung für Wörter wie Jareds Antwort .

 (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; })(); 

In AngularJS 1.3.2 wurde dem ngMock-Modul eine countWatchers Methode hinzugefügt:

 / **
  * @ngdoc Methode
  * @name $ rootScope.Scope # $ countWatchers
  * @Modul ngMock
  * @Beschreibung
  * Zählt alle Beobachter von direkten und indirekten Kindbereichen des aktuellen scopes.
  *
  * Die Beobachter des aktuellen Bereichs sind in der Zählung enthalten und so sind alle Beobachter von
  * Isolieren Sie Kindbereiche.
  *
  * @returns {Anzahl} Gesamtzahl der Beobachter.
  * /

  function countWatchers () 
    {
    var root = angular.element (Dokument) .injector (). get ('$ rootScope');
    var count = root. $$ Beobachter?  root. $$ watchers.length: 0;  // den aktuellen Bereich einschließen
    var pendingChildHeads = [root. $$ childHead];
    var currentScope;

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

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

    Anzahl der Rücksendungen;
    }

Verweise

  • AngularJS Changelog: 1.3.2

  • ngMock: $ rootScope.Scope. $ countWatchers

Ich habe den folgenden Code direkt aus der $digest function selbst genommen. Natürlich müssen Sie wahrscheinlich den Anwendungselement-Selektor ( document.body ) unten aktualisieren.

 (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')); 

Das sind die functionen, die ich verwende:

 /** * @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); }; })(); 

Diese function verwendet einen Hash, um denselben Bereich nicht mehrmals zu zählen.

Es gibt eine rekursive function, die von Lars Eidnes ‘Blog unter http://larseidnes.com/2014/11/05/angularjs-the-bad-parts/ veröffentlicht wurde , um die Gesamtzahl der Beobachter zu sammeln. Ich vergleiche das Ergebnis mit der hier geposteten function und der in seinem Blog geposteten function, die eine etwas höhere Nummer generiert hat. Ich kann nicht sagen, welcher genauer ist. Gerade hier als Querverweis hinzugefügt.

 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; 

HINWEIS: Möglicherweise müssen Sie für Ihre Angular-App “ng-app” in “data-ng-app” ändern.

Die Antwort von Plantian ist schneller: https://stackoverflow.com/a/18539624/258482

Hier ist eine function, die ich per Hand geschrieben habe. Ich habe nicht über rekursive functionen nachgedacht, aber das habe ich stattdessen getan. Es könnte schlanker sein, ich weiß es nicht.

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

Dann legen Sie dies in Ihrem höchsten Controller (wenn Sie einen globalen Controller verwenden).

 $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'); }; 

Und schließlich ein Buchmarkt:

 javascript:logScope();