在JavaScript中,有没有办法计算整个页面上角度表的数量?
我们使用Batarang,但并不总是符合我们的需求。我们的应用很大,我们有兴趣使用自动化测试来检查手表数量是否过多。
按控制器计算手表也很有用。
修改:这是我的尝试。它使用类别范围来计算所有内容中的手表。
(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;
})();
答案 0 :(得分:216)
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'
。
我做了同样的事情,除了我检查HTML元素的数据属性而不是它的类。我在这里跑你的:
得到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');
}
}
}
没有打印出来,所以我猜我的更好(因为它发现了更多的手表) - 但我缺乏亲密的角度知识,以确保我的确不是解决方案集的适当子集。 / p>
答案 1 :(得分:16)
我认为上面提到的方法是不准确的,因为它们将同一范围内的观察者数量加倍。这是我的书签版本:
https://gist.github.com/DTFagus/3966db108a578f2eb00d
它还显示了一些分析观察者的更多细节。
答案 2 :(得分:12)
这是一个基于检查范围结构而放在一起的hacky解决方案。它似乎“似乎”起作用。我不确定这是多么准确,它肯定取决于一些内部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 :(得分:10)
有一个新的Chrome插件可以随时自动显示当前的观察者总数和最后一次更改(+/-)...它非常棒。
https://chrome.google.com/webstore/detail/angular-watchers/nlmjblobloedpmkmmckeehnbfalnjnjk
答案 4 :(得分:9)
由于我最近在我的应用程序中遇到了大量的观察者,我发现了一个很棒的库,名为 ng-stats - https://github.com/kentcdodds/ng-stats。它具有最小的设置,并为您提供当前页面上的观察者数量+摘要周期长度。它还可以投影一个小的实时图表。
答案 5 :(得分:8)
在AngularJS 1.3.2中,在ngMock模块中添加了countWatchers
方法:
/** * @ngdoc method * @name $rootScope.Scope#$countWatchers * @module ngMock * @description * Counts all the watchers of direct and indirect child scopes of the current scope. * * The watchers of the current scope are included in the count and so are all the watchers of * isolate child scopes. * * @returns {number} Total number of watchers. */ function countWatchers() { var root = angular.element(document).injector().get('$rootScope'); var count = root.$$watchers ? root.$$watchers.length : 0; // include the current scope var pendingChildHeads = [root.$$childHead]; var currentScope; while (pendingChildHeads.length) { currentScope = pendingChildHeads.shift(); while (currentScope) { count += currentScope.$$watchers ? currentScope.$$watchers.length : 0; pendingChildHeads.push(currentScope.$$childHead); currentScope = currentScope.$$nextSibling; } } return count; }
<强>参考强>
答案 6 :(得分:7)
Words Like Jared's answer的小改进。
(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;
})();
答案 7 :(得分: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'));
&#13;
答案 8 :(得分: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);
};
})();
此函数使用哈希不会多次计算相同的范围。
答案 9 :(得分: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”。
答案 10 :(得分: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();
答案 11 :(得分:0)
这个问题有点晚了,但是我用这个
angular.element(document.querySelector('[data-ng-app]')).scope().$$watchersCount
只需确保使用正确的querySelector。