Làm thế nào để đếm tổng số đồng hồ trên một trang?


143

Có cách nào, trong JavaScript, để đếm số lượng đồng hồ góc trên toàn bộ trang không?

Chúng tôi sử dụng Batarang , nhưng nó không phải lúc nào cũng phù hợp với nhu cầu của chúng tôi. Ứng dụng của chúng tôi rất lớn và chúng tôi quan tâm đến việc sử dụng các bài kiểm tra tự động để kiểm tra xem số lượng đồng hồ có tăng quá nhiều hay không.

Nó cũng sẽ hữu ích để đếm đồng hồ trên cơ sở mỗi bộ điều khiển.

Chỉnh sửa : đây là nỗ lực của tôi. Nó đếm đồng hồ trong mọi thứ với phạm vi ng-class.

(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ỗi bộ điều khiển là dễ dàng. Mỗi người $scopecó một mảng người theo dõi $$ với số lượng người theo dõi trên bộ điều khiển đó (tốt, nếu bạn có một số lần lặp lại hoặc một cái gì đó tạo ra một phạm vi khác, điều đó không hoạt động tốt). Nhưng tôi nghĩ rằng không có cách nào để xem tất cả đồng hồ trong toàn bộ ứng dụng.
Jesus Rodriguez

Câu trả lời:


219

(Bạn có thể cần thay đổi bodythành htmlhoặc bất cứ nơi nào bạn đặt 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);
})();
  • Cảm ơn erilem đã chỉ ra câu trả lời này đã thiếu $isolateScopetìm kiếm và người theo dõi có khả năng bị trùng lặp trong câu trả lời / nhận xét của anh ấy / cô ấy.

  • Cảm ơn Ben2307 đã chỉ ra rằng 'body'có thể cần phải thay đổi.


Nguyên

Tôi đã làm điều tương tự ngoại trừ tôi đã kiểm tra thuộc tính dữ liệu của phần tử HTML chứ không phải lớp của nó. Tôi đã chạy của bạn ở đây:

http://fluid.ie/

Và nhận được 83. Tôi đã chạy của tôi và có 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);
})();

Tôi cũng đặt cái này trong mỏ:

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

Và không có gì được in ra, vì vậy tôi đoán rằng cái của tôi tốt hơn (trong đó nó tìm thấy nhiều đồng hồ hơn) - nhưng tôi thiếu kiến ​​thức góc cạnh thân mật để biết chắc chắn rằng cái của tôi không phải là một tập hợp con chính xác của bộ giải pháp.


2
Tôi đã cố gắng làm điều gì đó tương tự, và thấy đoạn trích này rất hữu ích. Một điều tôi nghĩ đáng để làm rõ là 'root' nên được đặt thành bất kỳ phần tử nào có thuộc tính 'ng-app', vì đó là nơi giữ $ rootScope. Trong ứng dụng của tôi, nó nằm trên thẻ 'html'. Chạy tập lệnh của bạn như đã bỏ lỡ những người theo dõi $ trong $ rootScope trong ứng dụng của tôi.
aamiri

Tôi đã tìm thấy một vấn đề với đoạn mã trên: nếu một phần tử con có phạm vi riêng, nhưng bây giờ người theo dõi, sẽ không phạm vi $. Người theo dõi có trả lại cho người theo dõi phạm vi cha mẹ không? Tôi nghĩ bạn nên thêm trước khi đẩy một cái gì đó như thế này (Tôi đang sử dụng lodash): if (! _. Chứa (watcher, watcher)) {watchers.push (watcher); }
Ben2307

2
Điều này nhận được tất cả các trình theo dõi được gắn liền với các yếu tố DOM. Nếu bạn loại bỏ một phần tử DOM, việc dọn dẹp liên kết $scope$$watchertự động, hoặc có một hiệu suất nào mà chúng phải chịu không?
SimplGy

Liệu nó có tính đến tính năng ràng buộc một lần mới? Số lượng của bạn bỏ qua loại ràng buộc này?
systempuntoout

10
Chỉ cần ngẩng cao đầu: Nếu bạn sử dụng $compileProvider.debugInfoEnabled(false);trong dự án của mình, đoạn trích này sẽ tính không có người theo dõi.
Michael Klöpzig

16

Tôi nghĩ rằng các phương pháp được đề cập là không chính xác vì họ tính người theo dõi trong cùng một phạm vi gấp đôi. Đây là phiên bản bookmarklet của tôi:

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

Nó cũng cho thấy một số chi tiết để phân tích người theo dõi.


12

Đây là một giải pháp hacky mà tôi kết hợp dựa trên việc kiểm tra các cấu trúc phạm vi. Nó "dường như" để làm việc. Tôi không chắc điều này chính xác đến mức nào và nó chắc chắn phụ thuộc vào một số API nội bộ. Tôi đang sử dụng 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);
    };

Điều này tương tự như giải pháp ban đầu của tôi (xem lịch sử chỉnh sửa). Tôi chuyển sang một cách tiếp cận khác bởi vì tôi nghĩ rằng việc đi theo hệ thống phân cấp phạm vi sẽ bỏ lỡ phạm vi cô lập.
ty.

3
Nếu tôi tạo một phạm vi tách biệt với $rootScope.$new(true)hoặc $scope.$new(true), nơi $scopedành cho bộ điều khiển, thì đi bộ phân cấp vẫn tìm thấy phạm vi đó. Tôi nghĩ rằng nó có nghĩa là các nguyên mẫu không được kết nối thay vì phạm vi không nằm trong hệ thống phân cấp.
Ian Wilson

Có, tất cả các phạm vi xuống từ $ rootScope, chỉ có sự kế thừa là "cô lập" trong phạm vi bị cô lập. Phạm vi biệt lập thường được sử dụng trong các chỉ thị - ở đây bạn không muốn các biến ứng dụng từ cha mẹ can thiệp.
markmarijnissen

Nếu bạn đang cố gắng phát hiện phạm vi mà bạn tạo nhưng không dọn sạch, phương pháp này là ưu việt. Trong trường hợp của tôi, việc thu thập dữ liệu DOM luôn hiển thị cùng một số phạm vi nhưng những phạm vi không gắn với DOM sẽ nhân lên trong vùng bóng tối.
SimplGy


9

Khi gần đây tôi đang vật lộn với số lượng người theo dõi cao trong ứng dụng của mình, tôi đã tìm ra một thư viện tuyệt vời, được gọi là ng-stats - https://github.com/kentcdodds/ng-stats . Nó có thiết lập tối thiểu và cung cấp cho bạn số lượng người theo dõi trên trang hiện tại + độ dài chu kỳ tiêu hóa. Nó cũng có thể chiếu một đồ thị thời gian thực nhỏ.


8

Cải thiện nhỏ cho câu trả lời như của 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
Vì bạn không sử dụng bộ chọn jQuery, bạn có thể sử dụng angular.element () thay vì $ ()
beardedlinuxgeek

8

Trong AngularJS 1.3.2, một countWatchersphương thức đã được thêm vào mô-đun ngMock:

/ **
 * phương pháp @ngdoc
 * @name $ rootScope.Scope # $ CountWatchers
 * @module ngMock
 * @sự miêu tả
 * Đếm tất cả những người theo dõi phạm vi con trực tiếp và gián tiếp của phạm vi hiện tại.
 *
 * Người theo dõi phạm vi hiện tại được bao gồm trong số đếm và tất cả những người theo dõi của
 * cách ly phạm vi con.
 *
 * @returns {number} Tổng số người theo dõi.
 * /

  chức năng CountWatchers () 
   {
   var root = angular.element (document) .injection (). get ('$ rootScope');
   var đếm = root. $$ người theo dõi? root. $$ watchers.length: 0; // bao gồm phạm vi hiện tại
   var chờ đợiChildHead = [root. $$ childHead];
   var currentScope;

   trong khi (chờChildHeads.length) 
    {
    currentScope = cấp phátChildHeads.shift ();

    trong khi (currentScope) 
      {
      đếm + = currentScope. $$ người theo dõi? currentScope. $$ người theo dõi.length: 0;
      Đang chờChildHeads.push (currentScope. $$ childHead);
      currentScope = currentScope. $$ nextSibling;
      }
    }

   số lần trả lại;
   }

Người giới thiệu


1
Cảm ơn! Tôi đã thay đổi angular.element(document)đến angular.element('[ng-app]')và đặt nó trong một bookmarklet với một cảnh báo:alert('found ' + countWatchers() + ' watchers');
undefined

4

Tôi lấy mã dưới đây trực tiếp từ $digestchính chức năng. Tất nhiên, có lẽ bạn cần cập nhật bộ chọn thành phần ứng dụng ( document.body) ở phía dưới.

(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

Đây là chức năng tôi sử dụng:

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

})();

Hàm này sử dụng hàm băm không để đếm cùng một phạm vi nhiều lần.


Tôi nghĩ element.data()đôi khi có thể không xác định được hoặc một cái gì đó (ít nhất là trên ứng dụng 1.0.5 mà tôi đã gặp lỗi khi chạy snipped này và cố gắng gọi countWatchers). Chỉ cần FYI.
Jared

1

Có một chức năng đệ quy được xuất bản bởi blog của Lars Eidnes tại http://larseidnes.com/2014/11/05/angularjs-the-bad-parts/ để thu thập tổng số người theo dõi. Tôi so sánh kết quả bằng cách sử dụng chức năng được đăng ở đây và chức năng được đăng trên blog của mình, đã tạo ra số lượng cao hơn một chút. Tôi không thể biết cái nào chính xác hơn. Chỉ cần thêm vào đây như một tài liệu tham khảo trên.

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;

LƯU Ý: bạn có thể cần thay đổi "ng-app" thành "data-ng-app" cho ứng dụng Angular của mình.


1

Câu trả lời của Plantian nhanh hơn: https://stackoverflow.com/a/18539424/258482

Đây là một chức năng mà tôi viết tay. Tôi đã không nghĩ về việc sử dụng các hàm đệ quy, nhưng đây là những gì tôi đã làm thay thế. Nó có thể gầy hơn, tôi không biết.

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

Sau đó đặt cái này vào bên trong bộ điều khiển cao nhất của bạn (nếu bạn sử dụng bộ điều khiển chung).

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

Và cuối cùng là một cửa hàng sách:

javascript:logScope();

0

Một chút muộn cho câu hỏi này, nhưng tôi sử dụng nó

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

chỉ cần chắc chắn rằng bạn sử dụng truy vấn chính xác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.