
Автор материала, перевод которого мы сегодня публикуем, полагает, что существует шесть концепций, глубокое знание которых нужно Angular-разработчикам для того чтобы создавать хорошо спроектированные приложения. При этом он говорит не об изучении исходного кода реализации этих концепций, хотя и ему самому иногда приходится заглядывать в код. Речь идёт о понимании соответствующих механизмов и об умении применять их на практике.
1. Архитектура, модули и библиотеки
В мире веб-разработки модульная архитектура Angular — это нечто особенное. Вероятно, это одна из таких идей, которые хуже других усваиваются новичками.
Самое сложно здесь заключается в том, что в веб-разработке уже используется модульная архитектура. Я, конечно, говорю об ES6-импортах.
Так как Angular-модули добавляют в систему дополнительный уровень логической группировки, важно, чтобы их структура как можно лучше соответствовала решаемым с их помощью задачам.
Знание о том, как разделять и объединять функционал приложения, пользуясь качественно спроектированными модулями, это фундаментальная часть создания архитектуры Angular-приложений.
?Разные типы Angular-модулей
Существуют различные типы Angular-модулей, о которых нужно знать:
- Declarations/Widget Module. Модули с объявлениями различных сущностей. В качестве примера подобных модулей можно привести наборы компонентов пользовательского интерфейса, директив, пайпов.
- Services Module. Модули сервисов. Например —
HttpClientModule
. - Routing Module. Модули маршрутизации.
- Domain Feature Module. Модули, реализующие ключевые задачи приложения.
- Core/Shared Module. Core-модуль — это модуль для объявления глобальных сервисов. Shared-модуль — это модуль, в котором объявляют компоненты для совместного использования.
Вот материал, в котором можно найти подробности об Angular-модулях.
?Библиотека или модуль?
Я сказал бы, что вышеозначенное различие между модулями можно распространить и на библиотеки. При таком подходе окажется, что может существовать библиотека, содержащая только сервисы, библиотека, представляющая маршрут, и так далее.
Но то, создают ли модуль или библиотеку, сильно зависит от типа проекта, и от того, представлен ли проект монорепозиторием или несколькими репозиториями.
?Вопросы, которые следует задать себе перед созданием модуля
Вот несколько вопросов, которыми стоит задаться перед написанием модуля:
- К какой именно разновидности модулей относится создаваемый модуль? Если вы не можете ответить на этот вопрос — это значит, что вам нужно поближе познакомиться с вышеперечисленными типами модулей. Весьма вероятно то, что при ответе на этот вопрос придётся упомянуть один или два типа модулей. В частности, речь идёт о модулях маршрутизации и модулях сервисов.
- Нужно ли оформить этот модуль в виде библиотеки, или он может быть обычным модулем? Ответ на этот вопрос найти немного сложнее. Я полагаю, что если используется монорепозиторий, то имеет смысл ориентироваться на разработку библиотек. Это, в долгосрочной перспективе, себя оправдает.
2. Разделение ответственности между компонентами, сервисами и директивами
Разделение ответственности — это, в теории, просто. А вот на практике это уже сложнее. Разработчики, со времён Angular.js, знали о том, что компоненты надо делать как можно компактнее, а сервисы стоит делать более масштабными. В новых версиях Angular эти идеи не претерпели особых изменений.
И в наши дни важно иметь представление о том, что именно должно входить в состав компонентов, что — в состав сервисов, а также учитывать то, что директивы, возможно, являются весьма сильно недооценённой возможностью Angular.
?Состояние
Ответ на вопрос о том, где именно хранить состояние компонента, зависит от того, где нужны соответствующие данные. А именно, они могут быть нужны только в компоненте, являясь локальными и инкапсулированными, или они могут быть нужны за пределами компонента.
- Если компоненты совместно используют состояние, или к состоянию нужно обращаться из сервисов, тогда состояние стоит хранить в сервисе. При этом, если состояние хранится в сервисе, не играет особой роли то, какие именно инструменты управления состоянием используются.
- Если состояние является локальным (например — речь идёт о форме) и используется только внутри компонента, тогда состояние стоит просто сохранить в компоненте.
?Работа с DOM
Вероятно, большинство манипуляций с DOM должно выполняться в директивах. Представим, что один из компонентов оснащают Drag-and-Drop-функционалом.
Уверен, вы в такой ситуации сможет создать компонент и осуществить привязку соответствующих событий из него, но, если так и сделать, будут смешаны два явления:
- Описание внешнего вида компонента.
- Определение поведения компонента.
Директивы — это возможность Angular, позволяющая описывать механизмы, предназначенные для многократного использования. Почти в каждом проекте, над которым мне довелось работать, я замечал недостаточно широкое использование директив. Директивы могут взять на себя немалую долю ответственности компонентов.
Вот вам упражнение: найдите в своём текущем проекте самый большой, по количеству строк кода, компонент. Используется ли в нём
Renderer
или ElementRef
? Соответствующая логика, скорее всего, может быть перенесена в директиву.3. Обнаружение изменений и рендеринг
Когда речь идёт о повторном рендеринге пользовательского интерфейса, то в Angular всё делается как по волшебству, с использованием внутренних механизмов фреймворка.
Но вот если нужно оптимизировать приложение так, чтобы повторный рендеринг интерфейса выполнялся бы только тогда, когда в этом есть необходимость, с этим «волшебством» приходится разбираться. А, улучшая рендеринг, приходится полагаться не только на знания, но и на интуицию.
Архитектору Angular-приложения, вероятно, стоит знать о том, что для оптимизации производительности рендеринга применяется стратегия обнаружения изменений
onPush
. Но в ходе работы всё не всегда идёт так, как ожидается. Особенно тогда, когда в шаблонах не используют наблюдаемые объекты и асинхронные пайпы.?Совершенствование обнаружения изменений
Для того чтобы улучшить процесс обнаружения изменений, используемый в проекте, есть смысл начать со следующих идей:
- Нужно рассматривать все данные как иммутабельные. Здесь могут очень пригодиться библиотеки для управления состоянием, основанные на Rx.
- Для вывода данных в шаблонах стоит использовать только (или преимущественно) наблюдаемые объекты. При использовании локального состояния стоит применять
BehaviorSubject
.
Если вы стремитесь к разработке высокопроизводительных Angular-приложений — вам просто необходимо очень хорошо разобраться с вопросами обнаружения изменений. Дело в том, что высокая производительность — это даже не «обновление интерфейса тогда, когда это нужно». Это — «обновление интерфейса только тогда, когда это нужно».
?Преодоление ограничений производительности Angular
Уменьшение количества повторных рендерингов интерфейса приложения — это один из секретов, позволяющих создавать быстрые и эффективные приложения. Но иногда производительность приложений должна выходить за границы, определяемые самим устройством Angular. Среди таких приложений можно отметить игры, проекты, данные которых часто обновляются, страницы, выводящие большие и сложные списки, и так далее.
Если вам и правда надо выжать из Angular абсолютный максимум производительности, это значит, что вам стоит прибегнуть к методике, предусматривающей избавление от Zone.js и точное обновление интерфейса с использованием свежих возможностей Ivy. Вот материал об этом.
4. Маршрутизация
Маршрутизация — это не только представление SPA в виде множества виртуальных страниц. Это ещё и загрузка бандлов приложения по запросу с использованием возможностей по ленивой загрузке материалов подсистемы маршрутизации Angular.
Если вы работаете над большим приложением и размеры бандлов этого приложения превышают 1 Мб, то вы, вероятно, уже знаете о том, почему это важно. И правда, никому не покажется заманчивой перспектива загрузки огромных объёмов данных для того чтобы поработать с неким приложением.
Маршрутизацию стоит использовать не только для того чтобы разделять маршруты верхнего уровня, но и для организации работы с более мелкими и глубокими частями интерфейса.
Это позволяет разбивать содержимое бандлов по основным маршрутам и помогает разделять приложения на небольшие части, которые не нужно передавать пользователям до тех пор, пока не будет сделан явный запрос на их загрузку.
?Пример: компонент с вкладками
Предположим, что мы разрабатываем пользовательский интерфейс, в котором используются вкладки. При этом каждая вкладка независима от других. Это — идеальная ситуация, в которой каждой вкладке можно назначить собственный маршрут и организовать ленивую загрузку данных, в ходе которой клиенту передаются только данные выбранной им вкладки.
Хотите ещё пример? Как насчёт всплывающих и модальных окон? Их код совершенно не нужно включать в состав материалов, входящих в первоначально загружаемый бандл проекта. Код таких окон есть смысл загружать только тогда, когда они нужны, но не раньше.
Если вам хочется, перед применением подобных идей, чем-то вдохновиться, предлагаю взглянуть на документацию компонента @angular/material/tabs, в котором реализован вышеописанный паттерн.
5. Формы
Большинство CRUD-приложений, в сущности, созданы из множества форм. Весьма вероятно то, что вы тратите очень много времени, создавая формы. Поэтому тому, кто хочет быть Angular-архитектором, важно как следует освоить работу с формами.
Большинство ваших форм, вероятно, будет использовать модуль
ReactiveFormsModule
. А если они не состоят из единственного элемента управления, то в них, с использованием ngModel
, будет реализована двусторонняя привязка данных.API Angular, предназначенный для работы с формами, довольно просто освоить. Для того чтобы достичь совершенства в использовании этого API, в общем-то, достаточно как следует изучить документацию и знать о том, какие проблемы могут возникать при работе с формами.
Главная проблема, о которой стоит знать, заключается в том, что формы в Angular не привязаны к типам данных, которые лежат в их основе. Это, вероятно, самое неприятное в работе с механизмами, которые, в остальном, сделаны очень хорошо. В результате оказывается, что разработчику нужно тщательно следить за тем, чтобы формы соответствовали структурам данных, которые используются при работе с ними.
6. RxJS
И последней в нашем списке, хотя — не последней по значимости, идёт технология RxJS.
Я убеждён в том, что одной из самых мощных возможностей Angular является глубокая интеграция этого фреймворка с Rx и с функциональным реактивным программированием.
Для того чтобы по-настоящему хорошо освоить Angular, открыв дорогу к проектированию качественных приложений, сначала нужно изучить Rx, или, по меньшей мере, самые важные операторы. Сложно быть по-настоящему продвинутым Angular-разработчиком, не потратив немало часов на то, чтобы понять Rx.
У того факта, что изучение Rx помогает в разработке Angular-приложений, есть две причины: производительность и асинхронная обработка данных.
Асинхронная обработка данных — это особенно сложная задача в современных, высокоинтерактивных приложениях. Поэтому стоит забыть о промисах, о
setTimeout
и о setInterval
, и начать работать в стиле Rx.Ещё одна серьёзная причина изучения Rx заключается в оптимизации производительности приложений. Конечно, для начала достаточно использовать асинхронные пайпы, но иногда этого недостаточно. Управлять повторным рендерингом компонентов, например, можно, пропуская через пайп только те события, возникновение которых подразумевает необходимость в повторном рендеринге.
Rx даёт разработчику множество операторов, которые способны помочь ему в кэшировании чего-либо, или в сборке чего-либо в пакеты. А это, как результат, ведёт к оптимизации производительности приложений. Вот материал о паттернах RxJS.
Итоги
Я привёл здесь небольшой список тем, которые стоит изучить тому, кто стремится стать высокоэффективным Angular-разработчиком, или тому, кто хочет быть архитектором Angular-приложений.
В этот список можно добавить ещё очень много всего. Но, кроме прочего, предлагаю не забывать о том, что для того чтобы по-настоящему хорошо изучить что-то, относящееся к миру веб-разработки, нужно начинать с основ. Это — JavaScript, CSS, паттерны проектирования, методики написания чистого кода, инструментарий и многое другое.
А что бы вы посоветовали изучить тем, кто хочет научиться проектировать качественные Angular-приложения?

riin41
А можете уточнить, как конкретно RxJs влияет на производительность? Это вроде бы больше про организацию асинхронного кода, само по себе это не увеличивает производительность
barba
Это перевод, поэтому, наверное, лучше задавать вопросы в оригинальной статье. =) Но вообще там раскрыто дальше:
Меньше ререндеров – больше производительность. Логика, видимо, такая.
lrsvolk
По большей части верное замечание, однако автор, вероятно, имел в виду следующие кейсы: