Привет! Меня зовут Даниил, я фронтенд-разработчик в Тинькофф Бизнесе. Мы строим удобные интерфейсы, чтобы клиенты могли быстро зарегистрировать бизнес.

Сегодня я хочу рассказать, почему мы используем Nx для всех наших Angular-проектов, какие проблемы решает этот инструмент и чем он лучше Angular CLI.

Что такое Nx

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

Больше о том, как использовать Nx, можно узнать на сайте. А мы рассмотрим следующие фичи, сравнив Angular CLI и Nx:

  1. Управление монорепозиториями.

  2. Инструменты из коробки.

  3. Кэширование артефактов.

  4. Миграция на новые версии технологий.

Управление монорепозиториями

Зачем вообще использовать монорепозитории и какие проблемы они решают? Об этом написано множество статей, но если вкратце, причин несколько:

  1. Переиспользование общих модулей и компонентов.

  2. Использование общих зависимостей одинаковых версий.

  3. Использование общих инструментов: тестирование, линтинг.

  4. Упрощение интеграций между проектами.

Angular CLI. Не предлагает специального подхода для работы в монорепозиториях. Теоретически можно хранить все приложения в Projects. Для небольших проектов этого может быть достаточно, но при масштабировании могут возникнуть проблемы.

Nx. Nx ориентирован на монорепозитории, поэтому есть ряд инструментов из коробки для удобной работы с ними:

— Project Boundaries. Если проект находится в монорепозитории, не исключено, что один проект может импортировать части другого. Неявные зависимости между проектами ведут к огромному количеству проблем, включая нарушение SRP, высокий coupling, сложности с масштабированием и безопасностью.

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

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

Граф зависимостей, генерируемый Nx
Граф зависимостей, генерируемый Nx

Инструменты из коробки

Каждый новый проект сопровождается выбором и настройкой типовых инструментов для юнит-тестирования, интеграционного тестирования, линтинга и других.

Интеграция инструмента с фреймворком — нетривиальная задача, и иногда разработчики тратят на нее много времени. Давайте разберем, какие инструменты предлагает Angular CLI, а какие — Nx.

Angular CLI. Сейчас при генерации проекта с помощью Angular CLI мы получаем только приложение. Юнит-тестирование предполагает использование Jasmine & Karma, но, начиная с последних версий, там нет конфигурации, а еще совсем недавно Karma стала deprecated. 

Что касается интеграционного тестирования и линтинга, то Protractor и TSLint тоже deprecated, поэтому в CLI их нет. Других альтернатив из коробки Angular CLI не предлагает.

Проект, сгенерированный через Angular CLI 15
Проект, сгенерированный через Angular CLI 15

Скорее всего, вы захотите как минимум использовать ESLint. Тогда придется найти инструмент и разобраться, что такое Angular ESlint и как его завести. Это легко, но не хочется сталкиваться с проблемой уже на этапе генерации проекта. 

Если вы решите писать юнит-тесты, придется затащить конфигурацию. А если захочется переехать с Jasmine & Karma на Jest, придется разобраться, как подружить Angular и Jest.

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

Nx. В Nx есть набор генераторов, которые позволяют генерировать сущности, добавлять необходимые зависимости и инструменты, а также проводить миграции в проекте. Например, конвертацию SCAM в Standalone-компоненты.

Сейчас при генерации Angular-проекта с помощью Nx вы получаете:

  • Angular-приложение;

  • Jest для юнит-тестирования;

  • Cypress для интеграционного тестирования;

  • ESLint для линтинга;

  • Prettier для форматирования кода.

Проект, сгенерированный через Nx 15
Проект, сгенерированный через Nx 15

С помощью официальных генераторов можно добавить и другие фичи: NgRx, Angular Universal, поддержку Module Federation, Storybook, Tailwind и многое другое. Набор технологий, поставляемых с приложением, позволяет не выбирать из бесконечного множества библиотек. Этим изначальным набором можно управлять с помощью флагов при генерации проекта. Например, убрать Jest или добавить Tailwind. 

При этом никто не запрещает использовать свой стек. Можно вручную настроить кастомные пакеты или использовать список коммьюнити плагинов для Nx, позволяющих добавить в проект фичи, которых нет в официальной версии. Например, stylelint, electron, ionic, playwright и другие.

Кэширование артефактов

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

Angular CLI. Предоставляет кэширование, построенное на основе решения из Webpack. Если сильно упростить, для каждого файла вычисляется хэш на основе его содержимого. Благодаря этому можно отслеживать, какие файлы были изменены, и пересобирать только их.

Проблема в том, что кэширование в Angular CLI ограничено только сборками и только одной машиной. Кроме того, оно медленнее, чем в Nx. 

Для примера возьмем небольшую библиотеку компонентов и дважды соберем ее с помощью Angular CLI.

Результаты сборки через Angular CLI
Результаты сборки через Angular CLI

Nx. В Nx кэширование — это не просто локальный кэш сборок, а кэш всех операций. То есть сборки, линтинг, тесты и любые кастомные операции. Кэширование в Nx построено на основе кэширования Angular CLI, но с добавлением своего кэша поверх всего этого. Таким образом, мы просто получаем улучшенный процесс из Angular CLI.

Давайте соберем ту же библиотеку с помощью Nx. Для первого билда разница несущественная: 10 545 против 9027 мс. Но с ростом проекта разрыв будет увеличиваться. Во втором случае благодаря улучшенному кэшированию разница приличная: 4902 против 62 мс.

Результаты сборки через Nx
Результаты сборки через Nx

Nx Cloud

Помимо локального кэширования Nx предлагает Nx Cloud, который сильно сокращает время сборок с помощью удаленного кэширования результатов сборок из-за шаринга артефактов между билд-агентами и машинами.

График ниже показывает, сколько времени удалось сэкономить с помощью Nx Cloud за последние 30 дней в репозитории Nx.

Время, сэкономленное с помощью Nx Cloud в Nx
Время, сэкономленное с помощью Nx Cloud в Nx

Результаты впечатляющие: Nx Cloud экономит больше половины времени для всех кэшируемых операций. 

Посмотрим еще на один пример: Taiga UI, UI Kit наших коллег. В Taiga UI через Nx Cloud кэшируются только build- и test-операции, поэтому на графике мы видим сэкономленное время только для них. Для e2e и lint кэширования нет — так сложилось исторически.

Время, сэкономленное с помощью Nx Cloud в Taiga UI
Время, сэкономленное с помощью Nx Cloud в Taiga UI

Здесь кэширование экономит примерно 30—40% времени. Показатели не такие высокие, как у Nx, но отчасти это зависит от кодовой базы и кэшируемых операций. 

В любом случае Nx Cloud можно подключить одной командой: это никак не повлияет на ваш воркфлоу, но вы сильно выигрываете во времени. 

Про кэширование можно почитать на сайте Nx на этой странице и на этой. А про Nx Cloud подробно рассказывал в докладе наш коллега Иван Ишмаметьев. 

Миграция на новые версии технологий

Технологии развиваются: что-то добавляется, что-то становится deprecated, иногда требуются мажорные изменения продуктов. Все это приводит к миграциям — неотъемлемой части разработки. И Angular CLI, и Nx предлагают миграции, но с разными подходами.

Angular CLI. У миграций через Angular CLI есть ряд критических проблем. О них подробно говорится в официальной документации Nx, поэтому рекомендую почитать ее для полноты картины.

Если вкратце, Angular CLI обновляет все и сразу. Это может быть недопустимо в больших проектах, так как миграции генерируют огромное количество изменений, которые невозможно контролировать. Возможности мигрировать частично нет. В результате мы всегда имеем огромный PR и высокие шансы что-то сломать.

Одна из проблем, о которой не говорят в документации, — автоматическое обновление только Angular. То есть все сопутствующие технологии — Jasmine, Protractor, NgRx — нужно обновлять вручную или отдельными сторонними скематиками.

Nx. Nx предлагает миграцию в два шага: поднятие версий в package.json и создание файла migrations.json, где указаны необходимые изменения. Разработчики сами выбирают, какие миграции произвести.

Например, мы хотим мигрировать с Angular 13 на Angular 14. Запускаем nx migrate, получаем обновленные package.json и список необходимых миграций. Дальше мы сами выбираем, что хотим сейчас мигрировать, а что нет.

Список миграций, сгенерированный Nx
Список миграций, сгенерированный Nx

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

Миграция NgRx, сгенерированная Nx
Миграция NgRx, сгенерированная Nx
Миграция Cypress, сгенерированная Nx
Миграция Cypress, сгенерированная Nx

Заключение

Nx можно назвать оберткой над Angular CLI. Используя Nx, вы ничего не теряете, а только приобретаете. 

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

Мы в Тинькофф полностью перешли на Nx, перестав поддерживать билдеры и скематики для Angular CLI. Nx умеет все, что умеет Angular CLI, но при этом добавляет множество фич. А нам не приходится поддерживать две огромные экосистемы.

Комментарии (5)


  1. kemsky
    30.05.2023 15:57
    +1

    Сначала я был удивлен, почему ангуляр выбрал карму, она была кривая насквозь вся. Потом сделали через сторонний пакет интеграцию с Jest, вроде бы норм, но со временем этот плагин становился все более проблемным, а его конфиг наполнился необъяснимой магией, пришлось вернуться на карму, которую к тому моменту починили. И тут снова парни не дают расслабиться и депрекейтят карму, хах!

    Ангуляр уже скоро сможет в esbuild, так что кеш и прочее скорее всего будет не актуален.


    1. ddubrava Автор
      30.05.2023 15:57
      +2

      Этим Nx и удобен, что он за вас настраивает инструменты эти. Касаемо кэша, не стоит забывать, что ботлнеком необязательно является сборка, а, например, сам прогон тестов (юниты или скришотное тестирование).


    1. t-mish
      30.05.2023 15:57

      Насчет последнего, думаю, что совсем скоро будет и возможность использовать его и в Nx/Angular проектах вместо Webpack. Привет nx/vite. А если уж не в моготу, можешь добавить Analogjs через генератор Nx. Красота


  1. t-mish
    30.05.2023 15:57
    +1

    Полностью согласен с утверждением что Nx это обертка над Angular CLI. Пока за последние два года работы на разных проектах с Nx не нашел того, что может Angular CLI и что не может сделать Nx. Огромные плюсы в виде мощных инструментов (graph, кэширование, контроля зависимостей и миграций). Все это окупается сторицей.


  1. Zy2ba
    30.05.2023 15:57
    +1

    А ещё у Nx более человеческие Executors и Generators вместо Builders и Schematics. Для пользователя конечно не сильно заметно, а вот при разработке чувствуется разница в удобстве. Отмечал это в выступлении на Angular Meetup. Пожалуй за исключением освещения этого момента начинка доклада супер-пересекается с этой статьёй)

    Алсо - c утверждением что Nx - это обёртка над Angular CLI не согласен. Это как-то его деклассирует. Всё же это куда большее широкий инструмент с поддержкой кучи других технологий помимо экосистемы ангуляра))