За последние годы Angular заметно эволюционировал: фреймворк пересматривает архитектурные решения, отказывается от привычных API и предлагает новые подходы к разработке. Эти изменения не всегда очевидны из документации, а их влияние на реальный процесс разработки становится понятным только на практике. В статье разобраны ключевые нововведения Angular за последние 2–3 года и опыт обновления проектов до Angular 20.
MDN Baseline badges и влияние на поддержку браузеров в Angular
Начать стоит не с самого Angular, а с важного обновления на сайте web-документации MDN (Mozilla Developer Network) Web Docs. Там появились Baseline badges, разработанные на основе данных W3C WebDX Community Group. Они позволяют быстро оценить целесообразность использования новой или незнакомой веб-фичи — например, части Browser API.
MDN использует данные о поддержке фич в четырех основных браузерах (Chrome, Edge, Firefox, Safari) и отображает один из трех бейджей:
- Limited availability — фича реализована не во всех браузерах
- Newly available — поддерживается всеми браузерами, но менее 2,5 лет
- Widely available — поддерживается всеми браузерами не менее 2,5 лет
Это заметно упрощает работу с документацией и снижает необходимость постоянно обращаться к caniuse.
Причина, по которой эта тема важна в контексте Angular, заключается в том, что с недавнего времени фреймворк формирует свой browserlist поддержки именно на основе widely available baseline. Перед релизом новой версии Angular ориентируется на набор браузеров примерно 30-месячной давности. Например, для Angular 20 используется этот baseline.
Такой подход может служить надежной точкой опоры при принятии решений о поддержке браузеров в проектах.
Standalone API и постепенный отказ от модулей
В Angular 15 API Standalone был помечен как stable с явной целью — заменить и со временем полностью вытеснить NgModule. Standalone-подход позволяет определять «модуль» с одним declaration в виде компонента, директивы или пайпа.
Согласно RFC, ключевые причины изменения — сокращение boilerplate-кода и улучшение tree shaking. Если улучшение tree shaking действительно возможно при полном отказе от NgModule, то тезис про уменьшение boilerplate выглядит спорным. В проектах среднего и крупного масштаба необходимость вручную указывать imports для каждого компонента может, наоборот, приводить к росту шаблонного кода.
Тем не менее сообщество в целом положительно восприняло RFC. Вероятная причина — первоначальное позиционирование Standalone как API, предназначенного для совместного использования с NgModule, особенно на фоне популярности SCAM-паттерна. Однако дальнейшее развитие показало иной вектор.
Хотя команда Angular долго утверждала, что NgModule останется опциональным, факты говорят о постепенном вытеснении:
- Angular 17 — standalone-проекты создаются по умолчанию
- Angular 19 — standalone: true автоматически добавляется всем компонентам, директивам и пайпам
- Появляются новые API (например, @defer), работающие только со standalone
Это формирует устойчивое ощущение, что NgModule в перспективе может быть помечен как deprecated.
Переход на standalone имеет и функциональные потери. Например, standalone-пайпы нельзя использовать как provider’ы. Кроме того, исчезает эффект самодокументирования структуры проекта, который давали отдельные файлы модулей — теперь архитектура сильнее «растворяется» в коде компонентов.
С учетом текущей динамики можно ожидать, что standalone станет стандартом де-факто. Однако сам подход к внедрению этого решения со стороны команды Angular вызывает вопросы и выглядит не самым аккуратным.
Inject вместо DI (dependency injection) в конструкторе
Функция inject, появившаяся в Angular 14, изначально воспринималась как шаг в сторону более функционального и реактивного подхода. Это было особенно заметно при переходе от классовых Route Guards к функциональным.
Со временем преимущества inject стали более очевидны. Один из ключевых кейсов — наследование. Использование inject избавляет от необходимости прокидывать зависимости через super, заменяя на protected параметр.
Дополнительный аргумент — изменения в ECMAScript 2022, которые привнесли стандартные class fields. Различия в тайминге инициализации полей могут вызывать проблемы с DI через конструктор. inject() полностью обходит эту проблему, что делает его future-proof решением.
Новый control flow в шаблонах
Angular 17 представил новый встроенный control flow, который пришел на смену ngIf, ngFor и ngSwitch (впоследствии помеченных как deprecated).
Ключевые изменения:
- Появились @else if и более читаемый else без ng-template
- У @for появился @empty, которое позволяет отображать кейс с пустым массивом.
- Упростился трекинг элементов — необходимость в trackBy-функциях возникает реже
- @switch остался близок к прежнему поведению
Новый синтаксис делает структуру ветвлений более наглядной, особенно в больших шаблонах, и повышает читаемость кода. Среди всех нововведений последних лет это изменение можно считать одним из самых удачных.
Signals
Signals — не новый концепт для фронтенда: его аналоги использовались в разных формах еще со времен Knockout. В Angular signals позиционируются как более легкая альтернатива Observable из RxJS.
Signals проще в управлении, лучше интегрированы во фреймворк и отлично подходят в качестве строительных блоков для OnPush change detection. В сочетании с effect, автоматически пересчитывающим callback при изменении зависимых сигналов, получается удобное API для создания компонентов с OnPush change detection’ом.
Билдеры: переход от Webpack к esbuild
Начиная с Angular 18, экосистема активно смещается от Webpack в сторону esbuild. Практика показывает кратный прирост скорости сборки и более простой developer experience.
Однако в реальных проектах переход возможен не всегда. Например, при использовании динамического Module Federation все еще приходится полагаться на Webpack custom builder. Альтернативные решения либо сталкиваются с проблемами производительности, либо не справляются со спецификой Angular-сборки.
Сейчас остается открытым вопрос поддержки и расширяемости билд-процесса, и здесь хотелось бы видеть больше внимания со стороны команды Angular.
SSR в Angular
Команда Angular Universal была объединена с основной командой фреймворка, а SSR теперь развивается в рамках пакета @angular/ssr. Это позволило отказаться от экспериментальных решений и, наконец, решить проблему регидратации.
Текущее решение выглядит production-ready и заметно стабильнее по сравнению с опытом использования SSR в Angular 16. Вектор развития в этом направлении можно оценить как однозначно позитивный.
Zoneless Change Detection
Zoneless Change Detection несколько версий находился в статусе developer preview и неожиданно стал production-ready в Angular 20.2.
Отказ от Zone.js — серьезный архитектурный шаг. Signals действительно упрощают управление CD, но остаются вопросы, связанные с заменой applicationRef.isStable и управлением макротасками, которые ранее использовались для контроля первого состояния стабильности приложения.
В качестве альтернативы предлагается afterNextRender, но пока неочевидно, покрывает ли он все реальные кейсы. Использование Zoneless на текущем этапе требует переосмысления механизмов определения готовности приложения, хотя практика может показать, что переход менее болезненный, чем кажется.
Взгляд в будущее
Angular До релиза Angular 21 ожидался период стабилизации после волны крупных изменений — и частично эти ожидания оправдались.
Можно предположить следующие тренды:
- Standalone окончательно закрепится в гайдах и best practices
- Интеграции с Signals продолжат развиваться и могут вытеснить RxJS из части сценариев
- Zoneless станет стандартом по умолчанию (что и произошло в Angular 21)
- Хочется верить, что появится больше возможностей для кастомизации билд-процесса
Скорее всего, дальнейшее развитие будет сосредоточено не столько на новых фичах, сколько на инструментах тестирования и стабилизации экосистемы.