Хотел бы поделиться небольшой заметкой о том, как ускорить выполнение $digest() путем замены стандартных директив эквивалентами, которые не вызывают $watch.
Смысл в том, что часто на странице очень много элементов для которых нужно выпонить биндинг данных только один раз используя данные моделей которые не изменяются, например:
<h1 ng-bind="l10n.main_title"></h1>
<a ng-href="/edit/{{user.id}}" ng-bind="user.name"></a>
Эти и другие стандартные директивы любезно будут проверять не изменилось ли значение выражения при каждом $digest. Исправить ситуацию можно набором довольно простых кастомных директив, например для текста:
app.directive(staticText', function() {
return {
restrict: "A"
link: function(scope, element, attrs) {
element.text(scope.$eval(attrs.customText));
}
};
})
<h1 custom-text="l10n.main_title"></h1>
Результат — минус один $watch.
Выдумывать свой велосипед конечно же не нужно, на GitHub лежат два достойных модуля:
Первый ($watch fighters) совсем простой, включает в себя такие директивы:
- set-title
- set-href
- set-text
- set-html
- set-class
- set-if
Второй же (Bindonce) интереснее, т.к. разработчики подумали о том, что данные для биндинга могут быть недоступны в момент рендеринга шаблона директивы (например, они подгружаются ajax-запросом). Выглядит это так:
<div bindonce="User">
<h1 bo-text="User.name"></h1>
</div>
Для родительской директивы bindonce создается времменый $watch, когда значение переданной в неё переменной становится отличным от undefined запускаются вложенные bo-* директивы, а временный $watch удаляется.
Модуль Bindonce включает:
- bo-if
- bo-show
- bo-hide
- bo-text
- bo-html
- bo-href
- bo-src
- bo-class
- bo-alt
- bo-title
- bo-id
- bo-style
- bo-value
- bo-attr bo-attr-foo
Более подробно можно почитать на странице репозитория, там довольно развернутый readme.
На последок приведу функцию, которую нашел здесь. Она примерно подсчитывает общее количество $watch'еров на странице.
(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);
})();
Ради интересы можно сравнить количество до и поле внедрения zero-watch директив.
Надеюсь кому то пригодится. Спасибо.
Автор: snater