...

воскресенье, 12 января 2014 г.

Избавляемся от лишних $watch'еров

Хотел бы поделиться небольшой заметкой о том, как ускорить выполнение $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 директив.

Надеюсь кому то пригодится. Спасибо.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий