Лучшие практики AngularJS

  • Tutorial
По мотивам этой трансляции.

Вместо предисловия скажу, что есть такой сайт yeoman.io, где собраны наиболее популярные технологии, автоматизирующие разработку фронтенда (сборку, параметризацию CSS и проч.). Обратите на него внимание в начале работы над проектом.

Использование директив


Некоторое количество разработчиков считают, что директивы захламляют HTML, так же им тяжело изменить старые привычки. Стоит заметить, что директивы существуют в HTML с незапамятных времен. Это всем хорошо знакомые элементы формы: поля ввода, радиокнопки, выпадающие списки и проч. Другое дело, что сделаны они «чтобы отстали». Эту проблему и решает Ангуляр.

Сравним обычный чекбокс
<input type="checkbox" name="option1" value="a1" checked>

и чекбокс в Ангуляре
<input type="checkbox" ng-model="myModel">

Что проще? name/id/class можно не указывать, потому что директивы не привязываются по имени. Начальное состояние задается моделью, поэтому checked не имеет смысла. Но это еще не всё. Последний код можно переписать и так
<input type="checkbox"  ng-model="myModel"  ng-true-value="on" ng-false-value="off"  ng-change="change()">

Думаю, многие в начале знакомства с HTML недоумевали почему подобного нет в стандартном варианте, почему чекбокс возвращает on или пустоту, а не true и false, почему можно изменить только значение on и много других почему.

Так вот, Ангуляр просто заставляет вести существующие интерактивные элементы понятным образом, унифицирует связь элементов с данным модели и на этой основе дает возможность создавать какие угодно собственные элементы управления, не дожидаясь прихода HTML5, HTML6 или HTML8.

Теперь рассмотрим директиву, аналогов которой в HTML нет
<ANY ng-repeat="book in books">

На лицо использование логики в тегах. На самом деле это не совсем так. Основная логика, а она гораздо сложнее, упакована в скрипт, описывающий директиву. В атрибут вынесен только интерфейс взаимодействия. Разработчики Ангуляра полагают (и с ними сложно не согласиться), что разметка должна говорить не только о том, КАКИЕ элементы расположены на странице, но так же о том, ЧТО они делают. А описание того КАК они это делают — удел скрипта.

Разумеется, в директиву можно запихать и сложную логику. Можно, вообще, практически любое поведение организовать из набора стандартных директив. Иногда это оправдано, особенно, когда нужно что-то сделать быстро, но Ангуляр не пропагандирует на 100% такой подход.

Все уже знают, что директивы можно записывать несколькими способами
<my-dir></my-dir>
<span my-dir="exp"></span>
<span class="my-dir: exp;"></span>
<!-- directive: my-dir exp -->

Так вот, наиболее предпочтительный (близкий к идеологии) первый способ. В ряде случаев (например, для директив, подобной ng-repeat, или для совместимости, в т.ч. психологической) — второй. Третий сделан просто так. Четвертый необходим, чтобы обойти ограничения HTML, например поместить что-либо отличное от <td> в тег <tr>. Так же для лучшей совместимости со старыми браузерами рекомендуется начинать имя директивы с какого-либо префикса (напр., my-). Для валидаторов можно добавлять к именам префиксы x- или data- (они будут отброшены Ангуляром).

Борьба со вспышками


Другими словами, как сделать загрузку страницы более гладкой для пользователя.

Во-первых, размещайте ангуляровские скрипты в конце страницы, чтобы пользователь не смотрел на белый экран, пока они грузятся.

Во-вторых, обратите внимание на директиву ng-cloak, которая скрывает шаблон до полной его обработки Ангуляром, а так же используйте ng-bind вместо выражения в фигурных скобках {{ }}.

На самом деле, в большинстве простых случаев проблем с мерцанием попросту не возникает.

Отличие контроллеров от сервисов


Контроллеры описывают поведение вида, т.е. отвечают на вопросы, что произойдет, если нажать на кнопку Х и где проводить работу с данными Х. Для каждого вида приложения создается отдельный экземпляр контроллера. В контроллерах ни в коем случае нельзя проводить манипуляции с DOM. Работа с DOM производится только в директивах.

Сервисы содержат основную логику и отвечают на вопрос что и как делает Х. В отличие от контроллеров, сервисы это синглтоны, т. е. во всем приложении может существовать только один экземпляр сервиса. В них так же не стоит работать с DOM, но иногда можно, например, если необходимо сделать глобальное диалоговое окно и т. п. В любом случае, использование DOM-манипуляций в сервисах необходимо ограничить.

Деградация говнокода


Из всего вышесказанного можно определить стратегию борьбы с говнокодом. Во время экспериментов, авралов и проч. простая логика может писаться прямо в HTML, по мере усложнения её необходимо переносить в отдельные директивы и контроллеры. Далее вся, не относящаяся к представлению логика, выносится в сервисы. Получается своего рода локальный рефакторинг.

Области видимости (scope)


Область видимости связывает вид с контроллером. Она предназначена только для чтения в шаблонах (виде) и только для записи в контроллерах.

Не стоит путать область видимости с моделью. Область видимости это ссылка на модель. Поэтому неправильно записывать параметры модели в область видимости напрямую. Необходимо создать один параметр и поместить туда модель.
//неправильно
$scope.param1 = 'hello';
$scope.param2 = 'world';

//правильно
$scope.model = {
  param1: 'hello',
  param2: 'world'
};

Разница в этих подходах наглядно демонстрируется в этом видео, а так же в первом примере (см. комментарии в коде).

Соответственно, обращение к модели в директивах чаще всего будет выглядеть так: и можно сформулировать правило: если в обращении к модели нет точки, значит что-то делается неправильно.

Модули


Не стоит разделять модули по особенностям реализации, т.е. на модуль для директив, модуль для контроллеров и т. д. Необходимо группировать модули по функциональности и схожести решаемых ими задач. Каждая библиотека многоразового использования должна быть собрана в один модуль.

При создании и использовании модуля стоит принять во внимание то, что, если он относится к обособленной части вида, то может быть лениво загружен (такая функциональность в планах). Поэтому распределите зависимости таким образом, чтобы он не вызывался когда в этом нет нужды.

Производительность


Для ускорения приложений
  • Минифицируйте и объединяйте ява-скрипты
  • Используйте GZIP-сжатие (может ускорить загрузку в два раза)
  • НЕ кэшируйте index.html, т.к. он содержит ссылки на подключаемые библиотеки и браузер, у которого этот файл закэширован, будет всегда ссылаться на их старые версии.
  • Кэшируйте (сохраняя версионность): шаблоны, код, картинки, CSS.

Пока Ангуляр не поддерживает компиляцию шаблонов на сервере и выдачу всей страницы одним запросом (в планах второй версии), поэтому для ускорения рекомендуется объединить все микрозапросы в один самостоятельно. А так же использовать кэширование шаблонов.

Что касается производительности клиентского кода, то она больше всего зависит от двух факторов:
  • Как много данных, требующих связывания, располагается на странице (как правило, они заключаются в {{ }}).
  • Насколько тяжелые выражения вычисляются для этих данных.

Не используйте медленные функции в $watch и в выражениях {{ }}. Так же не имеет значения сколько данных, требующих связывания, на странице, если в данный момент рендерится только один кусок.

Директивы ng-view и ng-include изменяют DOM-структуру приложения, поэтому не попадают в стандартный цикл перерисовки, тогда как ng-show и ng-hide структуру не меняют. С директивой ng-repeat для генерации списков нужно быть осторожнее, т. к., если при сотнях строк всё хорошо, то при тысячах могут начаться тормоза. В этом случае рекомендуется разбивать данные на страницы, использовать поиск и т. п. Можно использовать фильтры, но иногда они могут оказаться слишком дорогими, особенно, когда запускаются снова и снова. В этом случае можно создать вторую, внутреннюю модель. Сохранять в нее отфильтрованные данные из основной модели и уже вторую модель использовать в ng-repeat. Возможны и другие стратегии.

Для вставки шаблонов в DOM предпочтительнее использовать ng-include вместо innerHTML, т. к. она лучше оптимизирована.

Планы на будущее


В данный момент разработчики Ангуляра работают над ленивой загрузкой скриптов, а так же над реализацией пре-рендеринга страницы на сервере, что должно ускорить начальную загрузку, а так же способствовать индексации страницы поисковиками. Так же планируется упростить API директив и доработать анимацию, которая уже появилась в последних версиях фреймворка.
Поделиться публикацией

Похожие публикации

Комментарии 11
    +2
    Отличная статья. Очень приятно наблюдать активную популяризацию AngularJS. Уже больше года пользуюсь, никак не могу нарадоваться.

    В них так же не стоит работать с DOM, но иногда можно, например, если необходимо сделать глобальное диалоговое окно и т. п. В любом случае, использование DOM-манипуляций в сервисах необходимо ограничить.

    На самом деле (в случае глобального диалога, индикатора загрузки, оповещений и т.п) можно обойтись вообще без манипуляций DOM в сервисе. Ведь сервис это как канал взаимодействия между остальными частями Angular. Можно сделать директиву, которая зависит от сервиса и следит за его состоянием, и исходя из этого выполняет определенные DOM манипуляции. Состояние сервиса можно менять из контроллера или из другого сервиса, к примеру.
      0
      А можете поделиться какими-нибудь наработками? Очень не хватает готовых решений по статусам пользователей (в т. ч. разделению видимости шаблонов), интеграции с соц. сетями, комментариям, организации REST для древовидных структур и т. д. Можно статью на хабре написать, можно сюда залить: angular.ru/cookbook/ или, как самый простой вариант, сюда: javascript.ru/forum/angular/38293-igra-v-demki-piar-angulyara-i-obuchenie.html

      А то столько людей уже на Ангуляре, а примеров с гулькин нос)
        0
        Поделиться бы рад, да времени нет на статью. Могу ответить на конкретные вопросы, в stackoverflow режиме :) Пишите в личку, поговорим.
      0
      > разработчики Ангуляра работают над ленивой загрузкой скриптов, а так же над реализацией пре-рендеринга страницы на сервере

      Вопрос, есть js-фреймворки, которые это поддерживают уже сейчас?
        +1
        Да, есть derbyjs.com/
          +1
          Интересно, почему они все на Ноду заточены? Такое ощущение, что для сингл-пейдж приложений ПХП не существует
            0
            Заминусовали-то, как будто что-то против Ноды имею :-)
              +1
              Думаю, причина в том, что пик развития пхп-тулчейна миновал и он не совпал с ростом популярности одностраничных приложений. Вторая причина в том что одностраничные приложения требуют хорошего JS бэкграунда, а раз есть хороший JS-опыт то зачем еще учить PHP, лучше уж на ноде развивать серверную часть.
                0
                Уже подумываю, может самому нодой заняться, раз такое дело :-) Останавливает отсутствие вменяемых фреймворков на ней.
                  0
                  Среди сообщества nodejs большие комбайны не особо популярны. Тут народ любит собирать модули по кусочкам под задачу. Да и вся идеология платформы больше unix-way, когда множество мелких, но отлично выполняющих свою задачу, модулей взаимодействуют друг с другом. Не скажу что не будет попыток делать нечто рельсоподобное, но если в мире Java я с большим энтузиазмом принял Play! Framework и использовал его, то в мире nodejs даже не обращу на подобное особого внимания.
          0
          а так же способствовать индексации страницы поисковиками

          Для этого есть вот такая штука

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое