Избавляемся от лишних $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 директив.
Надеюсь кому то пригодится. Спасибо.

ссылка на оригинал статьи http://habrahabr.ru/post/208768/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *