Bir sayfadaki toplam saat sayısı nasıl hesaplanır?


144

JavaScript'te, tüm sayfadaki açısal saat sayısını saymanın bir yolu var mı?

Biz kullanmak Batarang , ama her zaman bizim ihtiyaçlarına uygun değil. Uygulamamız büyük ve saat sayısının çok yükselip yükselmediğini kontrol etmek için otomatik testler kullanmak istiyoruz.

Saatleri kontrolör başına saymak da yararlı olacaktır.

Düzenleme : İşte benim girişimim. Sınıf kapsamı ile her şeydeki saatleri sayar.

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

Kontrolör başına kolaydır. Her $scopebirinin o denetleyicideki izleyicilerin sayısı ile bir $$ watchers dizisi vardır (iyi bir tekrarlama veya başka bir kapsam oluşturan bir şey varsa, bu iyi çalışmaz). Ancak tüm uygulamadaki tüm saatleri görmenin bir yolu olmadığını düşünüyorum.
Jesus Rodriguez

Yanıtlar:


220

(Değişikliğin gerekebilir bodyiçin htmlya nerede olursanız olun koymak 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 sayesinde bu cevapta $isolateScopearama eksikti ve izleyiciler potansiyel olarak kendi cevabında / yorumunda kopyalanıyor.

  • 'body'Değiştirilmesi gerekebileceğine işaret ettiği için Ben2307'ye teşekkürler .


orijinal

Sınıf yerine HTML öğesinin veri özniteliğini kontrol etmem dışında aynı şeyi yaptım. Seninkini burada koştum:

http://fluid.ie/

Ve 83 aldım. Benimkini çalıştırdım ve 121 var.

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

Bunu benimkine de koydum:

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

Ve hiçbir şey basılmadı, bu yüzden benimkinin daha iyi olduğunu tahmin ediyorum (daha fazla saat bulduğunda) - ama benimkinin çözüm setinin uygun bir alt kümesi olmadığından emin olmak için samimi açısal bilgiye sahip değilim.


2
Benzer bir şey yapmaya çalışıyorum ve bu pasajı çok yararlı buldum. Açıklamaya değer olduğunu düşündüğüm bir şey, $ rootScope'un tutulduğu yer olan 'root' öğesinin 'ng-app' özniteliğine sahip olması gerektiği şekilde ayarlanması gerektiğidir. Benim app 'html' etiketinde. Komut dosyanızı çalıştığımda $ rootScope'daki $ watchers'ı kaçırdı.
aamiri

Yukarıdaki kodla ilgili bir sorun buldum: bir alt öğenin kendi kapsamı varsa, ancak şimdi gözlemcileri varsa, $ kapsamı olmaz. $$ gözlemcileri bu ana kapsamın gözlemcileri döndürür? Sanırım itmeden önce böyle bir şey eklemelisiniz (lodash kullanıyorum): if (! _. İçerir (gözlemci, izleyici)) {watchers.push (izleyici); }
Ben2307

2
Bu, DOM öğelerine bağlı tüm izleyicileri alır. Bir DOM öğesini kaldırırsanız, ilişkili $scopeve $$watcherotomatik olanın temizlenmesi mi yoksa gerçekleştikleri bir performans isabeti var mı?
SimplGy

Yeni bir defalık ciltleme özelliğini dikkate alıyor mu? Sayınız bu tür bir bağlamayı atlıyor mu?
systempuntoout

10
Sadece bir uyarı: Eğer $compileProvider.debugInfoEnabled(false);projenizde kullanırsanız, bu snippet sıfır izleyici sayar.
Michael Klöpzig


12

Kapsam yapılarını denetlemeye dayalı olarak bir araya getirdiğim hileli bir çözüm. Çalışmak "görünüyor". Bunun ne kadar doğru olduğundan emin değilim ve kesinlikle bazı dahili API'lara bağlı. Angularjs 1.0.5 kullanıyorum.

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

Bu benim orijinal çözümüme benziyor (düzenleme geçmişine bakın). Farklı bir yaklaşıma geçtim çünkü kapsam hiyerarşisinde yürümek ayrı kapsamları kaçıracağını düşünüyorum.
ty.

3
Denetleyici için $rootScope.$new(true)veya $scope.$new(true)ile nerede yalıtılmış bir kapsam oluşturursam $scope, hiyerarşiyi yürümek yine de bu kapsamı bulur. Bence bu kapsam hiyerarşide değil, prototiplerin bağlı olmadığı anlamına geliyor.
Ian Wilson

Evet, tüm kapsamlar $ rootScope'dan gelir, yalnızca yalıtılmış kapsamlarda kalıtım "izole edilir". İzole kapsamlar genellikle yönergelerde kullanılır - burada ebeveynlerden uygulama değişkenlerinin müdahale etmesini istemezsiniz.
markmarijnissen

Oluşturduğunuz ancak temizlemediğiniz kapsamları algılamaya çalışıyorsanız, bu yöntem üstündür. Benim durumumda, DOM'u taramak her zaman aynı sayıda kapsamı gösterir, ancak DOM'ya bağlı olmayanlar gölge arazide çoğalır.
SimplGy


9

Son zamanlarda başvurumda çok sayıda izleyici ile mücadele ederken, ng-stats adlı harika bir kütüphane buldum - https://github.com/kentcdodds/ng-stats . Minimum kurulumu vardır ve mevcut sayfadaki izleyici sayısını + özet döngü uzunluğunu verir. Ayrıca küçük bir gerçek zamanlı grafik yansıtabilir.


8

Kelimeler gibi Jared cevap küçük bir gelişme .

(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 seçicileri kullanmadığınız için $ () yerine
angular.element

8

AngularJS 1.3.2'de countWatchersngMock modülüne bir yöntem eklendi:

/ **
 * @ngdoc yöntemi
 * @name $ rootScope.Scope # $ count
 * @module ngMock
 * @açıklama
 * Mevcut kapsamın doğrudan ve dolaylı çocuk kapsamlarının tüm izleyicilerini sayar.
 *
 * Mevcut kapsamdaki gözlemciler sayıma dahildir ve tüm gözlemciler de
 * Çocuk kapsamlarını izole edin.
 *
 * @returns {number} Toplam izleyici sayısı.
 * /

  işlev countWatchers () 
   {
   var root = angular.element (document) .injector (). get ('$ rootScope');
   var count = root. $$ izleyici? kök. $$ watchers.length: 0; // geçerli kapsamı ekle
   var pendingChildHeads = [kök. $$ childHead];
   var currentScope;

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

    while (currentScope) 
      {
      count + = currentScope. $$ izleyici? currentScope. $$ gözlemcileri.uzunluk: 0;
      pendingChildHeads.push (currentScope $$ childHead.);
      currentScope = currentScope. $$ nextSibling;
      }
    }

   dönüş sayısı;
   }

Referanslar


1
Teşekkürler! Değiştim angular.element(document)için angular.element('[ng-app]')ve bir uyarı ile yer işareti uygulaması içine koyun:alert('found ' + countWatchers() + ' watchers');
tanımsız

4

Aşağıdaki kodu doğrudan $digestfonksiyonun kendisinden aldım . Tabii ki, muhtemelen alttaki uygulama öğesi seçicisini ( document.body) güncellemeniz gerekir .

(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

Kullandığım fonksiyonlar:

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

})();

Bu işlev, aynı kapsamı birden çok kez saymamak için bir karma kullanır.


element.data()Bazen tanımsız ya da bir şey olabilir düşünüyorum (en azından bu snipped koştu ve aramaya çalıştığımda bir hata var 1.0.5 bir uygulama countWatchers). Sadece FYI.
Jared

1

Toplam sayı izleyicisini toplamak için Lars Eidnes'in blogunda http://larseidnes.com/2014/11/05/angularjs-the-bad-parts/ adresinde yayınlanan yinelemeli bir işlev vardır . Sonucu, burada yayınlanan işlevi ve blogunda yayınladığı işlevi kullanarak biraz daha yüksek sayı ürettiğimden karşılaştırırım. Hangisinin daha doğru olduğunu söyleyemem. Sadece buraya bir referans olarak eklendi.

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;

NOT: Açısal uygulamanız için "ng-app" i "data-ng-app" olarak değiştirmeniz gerekebilir.


1

Plantian'ın cevabı daha hızlı: https://stackoverflow.com/a/18539624/258482

İşte el yazdığım bir fonksiyon. Özyinelemeli işlevleri kullanmayı düşünmedim, ama bunun yerine yaptım. Daha yalın olabilir, bilmiyorum.

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

Sonra bunu en yüksek kontrolörünüzün içine koyun (global bir kontrol cihazı kullanıyorsanız).

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

Ve son olarak bir yer işareti:

javascript:logScope();

0

Bu soruya biraz geç, ama bunu kullanıyorum

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

doğru querySelector kullandığınızdan emin olun.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.