จะนับจำนวนนาฬิกาทั้งหมดในหน้าได้อย่างไร


144

JavaScript มีวิธีในการนับจำนวนนาฬิกาเชิงมุมในหน้าทั้งหมดหรือไม่

เราใช้Batarangแต่ไม่เหมาะกับความต้องการของเราเสมอไป แอปพลิเคชันของเรามีขนาดใหญ่และเราสนใจที่จะใช้การทดสอบอัตโนมัติเพื่อตรวจสอบว่าจำนวนนาฬิกาเพิ่มขึ้นมากเกินไปหรือไม่

มันจะมีประโยชน์ในการนับจำนวนนาฬิกาบนพื้นฐานต่อคอนโทรลเลอร์

แก้ไข : นี่คือความพยายามของฉัน มันนับการเฝ้าดูทุกอย่างด้วยขอบเขตคลาส ng

(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มีอาร์เรย์ตัวเฝ้าดู $$ ที่มีจำนวนตัวเฝ้าดูบนตัวควบคุมนั้น (ถ้าคุณมี 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การค้นหาและผู้เฝ้าดูอาจถูกทำซ้ำใน / คำตอบ / ความคิดเห็นของเขา / เธอ

  • ขอบคุณ Ben2307 ที่ชี้ให้เห็นว่า'body'อาจต้องมีการเปลี่ยนแปลง


เป็นต้นฉบับ

ฉันทำสิ่งเดียวกันยกเว้นฉันตรวจสอบ data data ขององค์ประกอบ HTML แทนที่จะเป็น class ฉันวิ่งของคุณที่นี่:

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
ฉันพยายามทำสิ่งที่คล้ายกันและพบว่าข้อมูลนี้มีประโยชน์มาก สิ่งหนึ่งที่ฉันคิดว่าคุ้มค่าที่จะอธิบายคือ 'รูท' ควรตั้งค่าเป็นองค์ประกอบใดก็ตามที่มีแอททริบิวต์ 'ng-app' ตามที่เก็บไว้ที่ $ rootScope ในแอพของฉันอยู่ที่แท็ก 'html' การเรียกใช้สคริปต์ของคุณในลักษณะที่ไม่ควรพลาดคือผู้เฝ้าดู $ ใน $ rootScope ในแอปของฉัน
aamiri

ฉันพบปัญหาหนึ่งเกี่ยวกับโค้ดด้านบน: หากองค์ประกอบย่อยมีขอบเขตเป็นของตัวเอง แต่ตอนนี้ผู้เฝ้าดูจะไม่ $ $ $ $$ ผู้เฝ้าดูส่งคืนเป็นผู้ดูแลขอบเขตของผู้ปกครองหรือไม่ ฉันคิดว่าคุณควรเพิ่มก่อนกดบางสิ่งเช่นนี้ (ฉันใช้ lodash): ถ้า (! _. มี (นักดู, ผู้เฝ้าดู)) {watchers.push (ผู้เฝ้าดู); }
Ben2307

2
สิ่งนี้ทำให้ตัวเฝ้าดูทั้งหมดที่แนบกับองค์ประกอบ DOM หากคุณลบองค์ประกอบ DOM จะเป็นการล้างข้อมูลที่เกี่ยวข้อง$scopeและ$$watcherอัตโนมัติหรือมีการเข้าชมที่เกิดขึ้นหรือไม่
SimplGy

มันคำนึงถึงคุณสมบัติการผูกครั้งเดียวใหม่หรือไม่? การนับของคุณข้ามการผูกมัดแบบนี้หรือไม่?
systempuntoout

10
แค่หัวขึ้น: ถ้าคุณใช้$compileProvider.debugInfoEnabled(false);ในโครงการของคุณตัวอย่างนี้จะนับศูนย์เฝ้าดู
Michael Klöpzig

16

ฉันคิดว่าวิธีการดังกล่าวไม่ถูกต้องเนื่องจากพวกเขานับจำนวนผู้ดูในขอบเขตเดียวกันสองเท่า นี่คือบุ๊คมาร์คเวอร์ชันของฉัน:

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

สิ่งนี้คล้ายกับโซลูชันดั้งเดิมของฉัน (ดูประวัติการแก้ไข) ฉันย้ายไปใช้วิธีอื่นเพราะฉันคิดว่าการเดินตามลำดับชั้นของขอบเขตจะไม่สามารถแยกขอบเขตได้
ไท

3
ถ้าฉันสร้างขอบเขตแบบแยกส่วนด้วย$rootScope.$new(true)หรือ$scope.$new(true)ที่ไหน$scopeสำหรับตัวควบคุมการเดินลำดับชั้นยังคงพบขอบเขตนั้น ฉันคิดว่ามันหมายความว่าต้นแบบไม่ได้เชื่อมต่อแทนที่จะเป็นขอบเขตไม่ได้อยู่ในลำดับชั้น
Ian Wilson

ใช่ขอบเขตทั้งหมดลงมาจาก $ rootScope เฉพาะการสืบทอดเท่านั้นคือ "isolated" ในขอบเขตที่แยกออกมา ขอบเขตที่แยกมักใช้ในคำสั่ง - ที่นี่คุณไม่ต้องการให้ตัวแปรแอพจากผู้ปกครองรบกวน
markmarijnissen

หากคุณพยายามตรวจจับขอบเขตที่คุณสร้าง แต่ไม่ล้างวิธีนี้ดีกว่า ในกรณีของฉันการตระเวน DOM แสดงขอบเขตเท่ากันเสมอ แต่สิ่งที่ไม่ได้แนบกับ DOM นั้นจะทวีคูณใน shadowland
SimplGy

10

มีปลั๊กอิน chrome ใหม่ที่แสดงจำนวนผู้เฝ้าดูในปัจจุบันโดยอัตโนมัติและการเปลี่ยนแปลงครั้งล่าสุด (+/-) เมื่อใดก็ได้ในแอปของคุณ ... มันยอดเยี่ยมจริงๆ

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


นี่เป็นสิ่งที่ฉลาดมาก ๆ
Sujit Y. Kulkarni

9

ขณะที่ผมกำลังดิ้นรนกับเมื่อเร็ว ๆ นี้จำนวนมากของนักดูในใบสมัครของฉันเกินไปฉันพบห้องสมุดที่ดีเรียกว่าNG-สถิติ - 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
 * @description
 * นับจำนวนผู้เฝ้าดูทั้งหมดของขอบเขตลูกโดยตรงและโดยอ้อมของขอบเขตปัจจุบัน
 * * * *
 * ผู้เฝ้าดูขอบเขตปัจจุบันรวมอยู่ในการนับและผู้ติดตามทั้งหมดจะเป็นเช่นนั้น
 * แยกขอบเขตของเด็ก
 * * * *
 * @returns {number} จำนวนผู้ดูทั้งหมด
 * /

  ฟังก์ชั่น countWatchers () 
   {
   var root = angular.element (เอกสาร). injector (). get ('$ rootScope');
   var count = root นักดู $$? root. $$ watchers.length: 0; // รวมขอบเขตปัจจุบัน
   var pendingChildHeads = [root. $$ childHead];
   var currentScope;

   ในขณะที่ (pendingChildHeads.length) 
    {
    currentScope = pendingChildHeads.shift ();

    ในขณะที่ (currentScope) 
      {
      นับ + = currentScope แล้วผู้ดู $$ currentScope. $$ watchers.length: 0;
      pendingChildHeads.push (currentScope $$ childHead.);
      currentScope = currentScope $$ nextSibling;
      }
    }

   นับผลตอบแทน
   }

อ้างอิง


1
ขอบคุณ! ฉันเปลี่ยนangular.element(document)เป็นangular.element('[ng-app]')และวางไว้ใน bookmarklet พร้อมการแจ้งเตือน: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 ที่ฉันได้รับข้อผิดพลาดเมื่อฉันวิ่ง snipped นี้และพยายามโทรcountWatchers) เพียงแค่ FYI
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;

หมายเหตุ: คุณอาจต้องเปลี่ยน "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.