ページ上の時計の総数を数える方法は?


144

JavaScriptで、ページ全体の角度のある時計の数を数える方法はありますか?

私たちはバタランを使用しますが、それが常に私たちのニーズに合うとは限りません。私たちのアプリケーションは大きく、自動化されたテストを使用して、監視数が増えすぎていないかどうかを確認したいと考えています。

コントローラーごとに時計を数えることも役立ちます。

編集:これが私の試みです。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配列があり、そのコントローラーのウォッチャーの数が含まれています(ng-repeatや、別のスコープを作成する何かがあると、うまく機能しません)。しかし、アプリ全体ですべての時計を表示する方法はないと思います。
イエスロドリゲス

回答:


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要素のdata属性を確認した以外は、同じことを行いました。私はここであなたのものを走らせました:

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

上記のコードで問題が1つ見つかりました。子要素に独自のスコープがあるがウォッチャーになった場合、$ scope。$$ watchersは親スコープのウォッチャーを返しませんか?次のようなプッシュの前に追加する必要があると思います(私はlodashを使用しています):if(!_。contains(watchers、watcher)){watchers.push(watcher); }
Ben2307 2013

2
これにより、DOM要素に関連付けられているすべてのウォッチャーが取得されます。あなたはDOM要素を削除した場合、のクリーンアップが関連する$scope$$watcher自動、またはそれらが被るパフォーマンスヒットがあるのでしょうか?
SimplGy

新しいワンタイムバインディング機能は考慮されますか?あなたのカウントはこの種のバインディングをスキップしますか?
systempuntoout 2014年

10
ほんの一部です:$compileProvider.debugInfoEnabled(false);プロジェクトで使用する場合、このスニペットはウォッチャーをカウントしません。
マイケルクレプツィヒ2015

16

これらのアプローチは、同じスコープのウォッチャーを2倍に数えるため、不正確だと思います。これが私のブックマークレットのバージョンです。

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

また、ウォッチャーを分析するための詳細もいくつか示します。


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の子孫であり、継承のみが分離スコープで「分離」されます。分離スコープはディレクティブでよく使用されます-ここでは、親からのapp変数が干渉しないようにします。
markmarijnissen、2014

作成したがクリーンアップしないスコープを検出しようとしている場合は、この方法が優れています。私の場合、DOMをクロールすると常に同じ数のスコープが表示されますが、DOMに接続されていないスコープはシャドウランドで増加しています。
SimplGy 2014年


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ではcountWatchers、ngMockモジュールにメソッドが追加されました:

/ **
 * @ngdocメソッド
 * @name $ rootScope.Scope#$ countWatchers
 * @module 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) 
      {
      カウント+ = 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.