По мотивам этой трансляции.
Вместо предисловия скажу, что есть такой сайт 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-манипуляции в сервисах необходимо ограничить.
Деградация говнокода
Из всего вышесказанного можно определить стратегию борьбы с говнокодом. Во время экспериментов, авралов и проч. простая логика может писаться прямо в HTML, по мере усложнения её необходимо переносить в отдельные директивы и контроллеры. Далее вся, не относящаяся к представлению логика, выносится в сервисы. Получается своего рода локальный рефакторинг.
Области видимости (scope)
Область видимости связывает вид с контроллером. Она предназначена только для чтения в шаблонах (виде) и только для записи в контроллерах.
Не стоит путать область видимости с моделью. Область видимости это ссылка на модель. Поэтому неправильно записывать параметры модели в область видимости напрямую. Необходимо создать один параметр и поместить туда всю модель.
//неправильно
$scope.param1 = 'hello';
$scope.param2 = 'world';
//правильно
$scope.model = {
param1: 'hello',
param2: 'world'
};
Разница в этих подходах наглядно демонстрируется в этом видео, а так же в первом примере (см. комментарии в коде).
Соответственно, обращение к модели в директивах чаще всего будет выглядеть так: <input type="checkbox" ng-model="model.param">
и можно сформулировать правило: если в обращении к модели нет точки, значит что-то делается неправильно.
Модули
Не стоит разделять модули по особенностям реализации, т.е. на модуль для директив, модуль для контроллеров и т.д. Необходимо группировать модули по функциональности и схожести решаемых ими задач. Каждая библиотека многоразового использования должна быть собрана в один модуль.
При создании и использовании модуля стоит принять во внимание то, что, если он относится к обособленной части вида, то может быть лениво загружен. Поэтому распределите зависимости таким образом, чтобы он не вызывался когда в этом нет нужды.
Производительность
Для ускорения приложений
- Минифицируйте и объединяйте js-скрипты
- Используйте GZIP-сжатие (может ускорить загрузку в два раза)
- НЕ кэшируйте index.html, т.к. он содержит ссылки на подключаемые библиотеки и браузер, у которого этот файл закэширован, будет всегда ссылаться на их старые версии.
- Кэшируйте (сохраняя версионность): шаблоны, код, картинки, CSS.
Пока Ангуляр не поддерживает компиляцию шаблонов на сервере и выдачу всего одним запросом (в планах второй версии), поэтому для ускорения рекомендуется объединить все микрозапросы в один самостоятельно. А так же использовать кэширование шаблонов.
Что касается производительности клиентского кода, то она больше всего зависит от двух факторов:
- Как много данных, требующих связывания, располагается на странице (как правило, они заключаются в
{{ }}
). - Насколько тяжелые выражения вычисляются для этих данных.
Не используйте медленные функции в $watch
и {{тяжелые выражения}}
. Так же не имеет значения сколько данных, требующих связывания, на странице, если в данный момент рендерится только один кусок.
Директивы ng-view
и ng-include
изменяют DOM-структуру приложения, поэтому не попадают в стандартный цикл перерисовки, тогда как ng-show
и ng-hide
структуру не меняют. С директивой ng-repeat
для генерации списков нужно быть осторожнее, т. к., если при сотнях строк всё хорошо, то при тысячах могут начаться тормоза. В этом случае рекомендуется разбивать данные на страницы, использовать поиск и т. п. Можно использовать фильтры, но иногда они могут оказаться слишком дорогими, особенно, когда запускаются снова и снова. В этом случае можно создать вторую, внутреннюю модель. Сохранять в нее отфильтрованные данные из основной модели и уже вторую модель использовать в ng-repeat
. Возможны и другие стратегии.
Для вставки шаблонов в DOM предпочтительнее использовать ng-include
вместо innerHTML
, т. к. она лучше оптимизирована.
Планы на будущее
В данный момент разработчики Ангуляра работают над ленивой загрузкой скриптов, а так же над реализацией пре-рендеринга страницы на сервере, что должно ускорить начальную загрузку, а так же способствовать индексации страницы поисковиками. Так же планируется упростить API директив и доработать анимацию, которая уже появилась в последних версиях фреймворка.
Автор: tamtakoe