Сегодня мы публикуем вторую часть перевода статьи, которая содержит набор рекомендаций для Angular-разработчиков. В предыдущей части были представлены 11 советов, в этой мы рассмотрим ещё столько же.
1. Маленькие компоненты, подходящие для повторного использования
Находите в компонентах фрагменты, которые могут быть использованы повторно и создавайте на их основе новые компоненты. Делайте компоненты как можно более «глупыми», так как это позволит использовать их в большем количестве сценариев. «Глупый» компонент — это тот, который не содержит какой-либо специфической логики, работа которого зависит исключительно от предоставленных ему входных данных.
При работе с компонентами следует придерживаться следующего общего правила: самый глубоко вложенный компонент в дереве компонентов должен быть и самым «глупым».
▍Пояснения
Повторное использование компонентов уменьшает дублирование кода, что упрощает поддержку проекта и внесение в него изменений.
«Глупые» компоненты устроены проще, чем другие, поэтому менее вероятно то, что в них появятся ошибки. Использование таких компонентов заставляет разработчика лучше обдумывать открытое API компонента и помогает решать проблемы.
2. О задачах, решаемых компонентами
Всегда, когда это возможно, избегайте наличия в компонентах логики, отличной от той, которая используется для визуализации данных.
▍Пояснения
Компоненты созданы как средства для представления информации и для управления тем, как должны работать интерфейсы. Любую бизнес-логику следует извлекать из компонентов и помещать, когда этого возможно, в отдельные методы или сервисы, отделяя таким образом бизнес-логику от логики представления информации.
Бизнес-логика, при представлении её в виде отдельных сервисов, обычно легче поддаётся модульному тестированию. При таком подходе она может быть повторно использована разными компонентами, которым нужны одинаковые возможности.
3. О больших методах
Если код метода оказывается очень длинным, обычно это указывает на то, что такой модуль решает слишком большое количество задач. Такой метод, как самостоятельная сущность, возможно, решает лишь одну задачу, но внутри него может присутствовать код, выполняющий несколько различных операций. Подобный код можно оформить в виде отдельных методов, каждый из которых, опять же, должен решать какую-то одну задачу, после чего следует использовать эти методы вместо соответствующих фрагментов кода в исходном методе.
▍Пояснения
Длинный код методов сложно читать, понимать и поддерживать. Кроме того, подобные методы подвержены ошибкам, так как изменение чего-то одного может повлиять на множество других механизмов метода. Длинный код означает усложнение рефакторинга, а эта операция весьма важна в любом приложении.
В этой связи можно упомянуть такой показатель как цикломатическая сложность программы. Существуют правила TSLint для выявления цикломатической сложности, которые можно использовать в проекте для того, чтобы избегать ошибок, которые могут появиться в слишком сложном коде. С их помощью можно оценивать качество кода и предотвращать возможные проблемы с его поддержкой.
4. Принцип DRY
DRY (Do not Repeat Yorself, не повторяйтесь) — это принцип, следуя которому следует стремиться к тому, чтобы в разных местах проекта не было бы копий одних и тех же фрагментов кода. Подобные фрагменты кода стоит оформить в виде самостоятельных сущностей, методов, например, и использовать их вызовы там, где раньше применялся повторяющийся код.
▍Пояснения
Если в разных местах приложения используется один и тот же код, это ведёт к тому, что если понадобится внести изменения в этот код, делать это придётся во множестве мест. В результате программу будет сложнее поддерживать, она окажется подверженной возникновению ошибок, связанных, например, с тем, что не все фрагменты одинакового кода оказались правильно отредактированными. Это, кроме того, увеличивает время, необходимое для внесения изменений в код и для его тестирования.
Если вы столкнулись с чем-то подобным — оформите повторяющийся код в виде самостоятельной сущности и используйте её вместо повторяющихся фрагментов. Это приведёт к тому, что для внесения изменений правки придётся вносить лишь в одном месте, а при тестировании нужно будет тестировать лишь один участок кода, а не несколько. Кроме того, чем меньше в приложении дублирующегося кода — тем быстрее оно будет загружаться пользователями.
5. Механизмы кэширования
При выполнении обращений к различным API можно заметить, что ответы от некоторых из них почти никогда не меняются. В подобных случаях можно добавить в программу средства кэширования данных и хранить в кэше то, что возвращают подобные API. При выполнении очередного запроса можно обратиться к кэшу, и, если в нём уже есть то, что нужно, можно воспользоваться этими данными, а если того что нужно нет — выполнить настоящий запрос и поместить в кэш ответ на него.
Если данные, получаемые из неких API, меняются с некоторой периодичностью, можно кэшировать их на определённое время. В результате, выполняя запрос к таким API, можно будет решить — откуда брать нужные данные.
▍Пояснения
Наличие кэширующего механизма позволяет избегать выполнения ненужных запросов к API. Если в приложении обращения к API выполняются только при наличии крайней необходимости, и данные, которые приложение уже получало, не запрашиваются повторно, улучшается производительность решения, в частности, из-за того, что ему не приходится постоянно ждать ответов от неких сетевых сервисов. Такой подход позволяет, кроме того, экономить трафик, так как одни и те же данные не загружаются снова и снова.
6. Логика в шаблонах
Если в ваших шаблонах имеется хоть какая-то логика, даже простое выражение &&
, эту логику следует извлечь и поместить в соответствующий компонент.
▍Пояснения
Если в шаблоне имеется логика, это означает, что такой шаблон невозможно подвергнуть модульному тестированию, и, таким образом, возрастает вероятность возникновения ошибок при изменении кода шаблона.
▍До
// шаблон
<p *ngIf="role==='developer'"> Status: Developer </p>
// компонент
public ngOnInit (): void {
this.role = 'developer';
}
▍После
// шаблон
<p *ngIf=«showDeveloperStatus»> Status: Developer
// компонент
public ngOnInit (): void {
this.role = 'developer';
this.showDeveloperStatus = true;
}
7. Работа со строками
Если у вас есть переменная строкового типа, которая может иметь значения из ограниченного набора, то, вместо того, чтобы объявлять её в виде обычной строки, задайте при её объявлении список возможных значений.
▍Пояснения
Продуманное описание типа переменной позволяет упростить обнаружение и устранение ошибок, так как при таком подходе они выявляются на этапе компиляции программы а не во время выполнения кода.
▍До
private myStringValue: string;
if (itShouldHaveFirstValue) {
myStringValue = 'First';
} else {
myStringValue = 'Second'
}
▍После
private myStringValue: 'First' | 'Second';
if (itShouldHaveFirstValue) {
myStringValue = 'First';
} else {
myStringValue = 'Other'
}
// тут появится следующая ошибка
Type '"Other"' is not assignable to type '"First" | "Second"'
(property) AppComponent.myValue: "First" | "Second"
8. Об управлении состоянием приложения
Рассмотрите возможность использования @ngrx/store для управления состоянием вашего приложения и @ngrx/effects для реализации побочных эффектов. Изменения состояния описываются действиями и выполняются чистыми функциями, называемыми редьюсерами.
▍Пояснения
@ngrx/store изолирует логику, имеющую отношение к состоянию приложения в одном месте и делает её единообразной в масштабах всего проекта. Тут, кроме того, имеется механизм мемоизации, действующий при работе с хранилищем, который повышает производительность приложения. Механизм @ngrx/store, скомбинированный со стратегией обнаружения изменений Angular ведёт к созданию более быстрых приложений.
9. Об иммутабельности состояния приложения
При использовании @ngrx/store рассмотрите возможность применения библиотеки ngrx-store-freeze для того, чтобы сделать состояние приложения иммутабельным. Эта библиотека предотвращает мутации состояния, выдавая исключения. Это позволяет избежать случайных изменений состояния приложения, которые ведут к непредсказуемым последствиям.
▍Пояснения
Изменение состояния приложения в компонентах ведёт к тому, что поведение приложения меняется в зависимости от порядка загрузки компонентов. Это нарушает ментальную модель шаблона redux. В результате изменения могут быть переопределены если состояние хранилища меняется и последующая работа с ним ведётся уже с использованием нового состояния. Здесь применим принцип разделения ответственности, в соответствии с которым компоненты находятся на уровне представления и им не следует знать о том, как менять состояние приложения.
10. Инструменты для тестирования приложений
Пользуйтесь специализированными средствами для тестирования приложений. Среди них можно отметить Jest и Karma.
▍Пояснения
Jest — это фреймворк для модульного тестирования приложений, разработанный Facebook. Он поддерживает механизмы параллельного выполнения тестов, что ускоряет работу. Благодаря тому, что он способен учитывать изменения кодовой базы, в очередном сеансе тестирования выполняются только испытания, касающиеся изменённого кода. Это улучшает возможности по анализу результатов тестирования. Кроме того, Jest предоставляет метрики покрытия кода тестами и поддерживается редактором VS Code и IDE WebStorm.
Средство для запуска тестов Karma разработано командой AngularJS. Ему, для запуска тестов, нужны реальные браузер и DOM. Karma позволяет выполнять тесты в различных браузерах. Jest, в свою очередь, не нуждается, например, в браузере Chrome без пользовательского интерфейса или в phantom.js, так как для работы этого фреймворка нужна лишь платформа Node.js.
11. Серверный рендеринг
Если вы пока ещё не пользуетесь технологией Angular Universal, то сейчас самое время для того, чтобы её испытать. Она позволяет организовать серверный рендеринг Angular-приложений. Как результат, пользователю передаются статические, заранее отрендеренные HTML-страницы. Это делает приложения невероятно быстрыми, так как их содержимое появляется в браузерах почти мгновенно из-за того, что браузеру не приходится загружать и разбирать JS-бандлы и тратить время на обеспечение работы механизмов Angular.
Кроме того, Angular-приложения, отрендеренные на сервере, обладают преимуществами перед обычными приложениями в плане CEO. Angular Universal создаёт статические страницы, а это облегчает задачу поисковых систем по индексированию таких страниц, так как им не приходится, для формирования страниц, выполнять JavaScript-код.
▍Пояснения
Благодаря использованию Angular Universal можно чрезвычайно сильно повысить производительность приложений. Недавно мы обновили наше приложение, добавив в него возможности серверного рендеринга. Это привело к сокращению времени загрузки сайта с нескольких секунд до десятков миллисекунд.
Кроме того, благодаря такому подходу страницы корректно отображаются в областях предварительного просмотра содержимого, используемых в социальных сетях. Время первого значимого отображения страницы (First Meaningful Paint) оказывается весьма небольшим, что избавляет пользователей от ненужного ожидания.
Итоги
Разработка приложений — это путь, проходя который программист постоянно находит возможности для улучшения того, что он создаёт. Вы, идя по этому пути, вполне можете воспользоваться приведёнными здесь советами по оптимизации Angular-приложений. Мы уверены, что их последовательное применение пойдёт на пользу любой команде разработчиков, а то, что получится в результате, понравятся пользователям, так как, с их точки зрения, это приведёт к тому, что они будут работать с более стабильными и производительными приложениями.
Уважаемые читатели! Если вы можете поделиться с сообществом Angular-разработчиков полезными советами по улучшению приложений — просим вас это сделать.
Автор: ru_vds