Angular 19 здесь, что нового?
Всем привет🤝, меня зовут Данила и я разработчик на Ангуляр. Далеко не все успели мигрировать на 18-ю версию, а тут ребята из тёплого Саннивилла радуют нас новой версией любимого фреймфорка. Давайте разбираться, что появилось!
routerOutletData
Теперь можно передавать данные родительскому компоненту RouterOutlet
, что упрощает обмен данными из родительского компонента с его вложенными дочерними элементами.
Затем в дочернем компоненте вы можете получить данные через DI и токен ROUTER_OUTLET_DATA
:
readonly userModel = inject>(ROUTER_OUTLET_DATA);
Service Workers
В поддержку сервис-воркеров в Angular было добавлено несколько функций.
Во-первых, теперь можно указать maxAge
для всего приложения с помощью новой опции конфигурации под названием applicationMaxAge
. Это позволяет нам настроить, как долго сервис-воркер будет кэшировать любые запросы. В пределах applicationMaxAge
файлы будут обслуживаться из кэша. Все запросы будут обслуживаться только из сети, это может быть особенно полезно для index.html
, чтобы гарантировать последнюю версию приложения, а не старую кэшированную версию, если пользователь пользовался приложением ранее.
{ "applicationMaxAge": "1d6h" // 1 день и 6 часов}
Во-вторых, появилась возможность определять refreshAhead
задержку для конкретной группы данных. Когда время до истечения срока действия кэшированного ресурса меньше этой refreshAhead
задержки, Angular обновляет ресурс.
{ "dataGroups": [ { "name": "api-books", "urls": ["/api/books/**"], "cacheConfig": { "maxAge": "1d", "timeout": "10s", "refreshAhead": "10m" } } ]}
Запрос и ответ через DI
Теперь получить доступ к объектам запроса и ответа в компонентах во время SSR стало легче благодаря новым токенам в @angular/core
:
REQUEST
для доступа к текущему объекту HTTP-запросаREQUEST_CONTEXT
для передачи пользовательских метаданных или контекста, связанного с текущим запросом, при рендеринге на стороне сервераRESPONSE_INIT
для доступа к опциям инициализации ответа
Гидратация
Angular 19 представляет частичную и инкрементную гидратацию, преобразующую производительность приложений с рендерингом на сервере (SSR).
Частичная гидратация: отдает приоритет загрузке критически важных компонентов, сокращая начальное время взаимодействия
Инкрементная гидратация: откладывает загрузку функций на основе взаимодействия с пользователем (щелчков или наведений), оптимизируя использование ресурсов
Эти методы позволят создавать более быстрые и интерактивные приложения, улучшая взаимодействие с пользователем.
ng generate component --export-default
Новая опция изменяет компонент на использование синтаксиса экспорта по умолчанию: export default class AuthComponent
. Это может быть интересно для компонентов с отложенной загрузкой:
loadComponent: () => import('./auth/auth.component')// вместо обычногоloadComponent: () => import('./auth/auth.component').then(m => m.AuthComponent)
Строгий CSP
В команду добавлен новый параметр, ng build
позволяющий включить строгую политику безопасности контента (CSP) в сгенерированном index.html
файле. Этот параметр применяет рекомендации из этой статьи Web.dev и включает автоматическое создание CSP на основе хэша на основе сценариев в index.html
файле.
{ "security.autoCsp": true // angular.json}
Неиспользованный импорт
В компилятор Angular добавлена расширенная диагностика, позволяющая обнаруживать неиспользуемый импорт в автономных компонентах!
Если вы забудете удалить импорт после рефакторинга кода, вы увидите такое сообщение:
TS-998113: Imports array contains unused imports [plugin angular-compiler]src/app/user/users.component.ts:14:27: 14 │ imports: [UserComponent, UnusedComponent], ~~~~~~~~~~~~~~~
Signal API
Почти все Signal API стали стабильными (effect
, toObservable
и toSignal
всё ещё нестабильны). Для удобства разработчиков сделали автомиграцию, выглядит это так (по отзывам, работает довольно хорошо)
ng generate @angular/core:signals? Which migrations do you want to run? (Press to select, to toggle all, to invert selection, and to proceed)❯◉ Convert `@Input` to the signal-based `input` ◉ Convert `@Output` to the new `output` function ◉ Convert `@ViewChild`/`@ViewChildren` and `@ContentChild`/`@ContentChildren` to the signal-based `viewChild`/`viewChildren` and `contentChild`/`contentChildren`
Все эффекты больше не обрабатываются одинаково. Angular различает два типа эффектов:
эффекты компонентов, создаваемые в компонентах или директивах;
корневые эффекты, которые создаются в корневых службах или с помощью этой
forceRoot
опции.
Эффекты компонентов теперь запускаются во время обнаружения изменений (непосредственно перед фактическим обнаружением изменений хост-компонента), а не после него, как это было раньше. Таким образом, вы можете увидеть некоторые изменения в их поведении, например, когда эффект запускается изменением сигнала запроса представления. Чтобы решить эту проблему, была добавлена новая функция afterRenderEffect
. Она похож на effect
, но её функция выполняется после рендеринга, а не до него. Как afterRender
и afterNextRender
, она также может указывать, что должно выполняться на каждой фазе рендеринга, но значения передаются от фазы к фазе как сигналы, а не как простые значения. В результате более поздние этапы могут не выполняться, если значения, возвращенные более ранними этапами, не изменяются. afterRender
функции еще находятся в предварительной версии для разработчиков.
Корневые эффекты не привязаны к жизненному циклу компонента и обычно используются для синхронизации состояния приложения с внешним миром (например, для записи его в локальное хранилище). Эти эффекты выполняются не как часть обнаружения изменений, а как микрозадача (и могут быть активированы в тестах с использованием TestBed.flushEffects()
).
Ресурсы
Resources API является экспериментальным и скоро пройдет процедуру RFC: я бы пока не советовал вам его использовать.
Функция resource
принимает объект с обязательной функцией-загрузчиком, которая возвращает обещание:
list(): ResourceRef | undefined> { return resource({ loader: async () => { const response = await fetch('/books'); return (await response.json()) as Array; } });}
В этом примере используется не HTTP-клиент, а собственная fetch()
функция, возвращающая обещание. Действительно, resource()
функция не связана с RxJS и поэтому может использовать любой клиент, возвращающий обещания. rxResource
является альтернативой resource
, его можно использовать с клиентом на основе Observable. Это пример отделения Angular от RxJS, но при этом обеспечивающего функции взаимодействия, позволяющие беспрепятственно его использовать.
Функция возвращает ResourceRef
объект, содержащий:
сигнал
isLoading
, указывающий, загружается ли ресурс;сигнал
value
, содержащий результат обещания;сигнал
error
, содержащий ошибку, если обещание отклонено;сигнал
status
, который содержит статус ресурса.
Затем вы можете использовать эти сигналы в своем шаблоне:
@if (booksResource.isLoading()) { Loading...
} @else { @for (book of booksResource.value(); track book.id) { {{ book.name }}
}}
Сигнал status
может быть:
ResourceStatus.Idle
, исходное состояниеResourceStatus.Loading
, когда обещание находится на рассмотренииResourceStatus.Error
, когда обещание отклоненоResourceStatus.Resolved
, когда обещание выполненоResourceStatus.Reloading
, когда ресурс перезагружаетсяResourceStatus.Local
, когда значение установлено локально
Ресурс также имеет reload
метод, позволяющий перезагрузить ресурс. В этом случае его статус будет установлен на ResourceStatus.Reloading
.
*_INITIALIZER токены устарели
Вместо токена APP_INITIALIZER
теперь нужно использовать provideAppInitializer
. Эта новая функция — более элегантный способ предоставить инициализатор вашему приложению.
До версии 19 вы могли бы предоставить такой инициализатор:
{ provide: APP_INITIALIZER, useValue: () => inject(InitService).init(), multi: true},
Теперь вам нужно использовать provideAppInitializer
:
provideAppInitializer(() => inject(InitService).init())
ENVIRONMENT_INITIALIZER
и PLATFORM_INITIALIZER
также устарели в пользу provideEnvironmentInitializer
и providePlatformInitializer
.
Руками ничего менять не нужно, автомиграция позаботится об этом =)
Шаблоны
Синтаксис @let
теперь стабилен. Выражения в шаблоне теперь могут использовать typeof
оператор:
@if (typeof user === 'object') {}
У пайпа keyvalue
также есть новая опция. Этот пайп перебирать записи объекта. Но, как это ни удивительно, он по умолчанию упорядочивает записи по их ключу. Вы уже могли передать функцию сравнения, но теперь вы можете передать null,
чтобы отключить упорядочивание:
@for (entry of bookModel() | keyvalue: null; track entry.key) { {{ entry.key }}: {{ entry.value }}}
HMR и сборка
Hot Module Replacement теперь по-умолчанию работает и для стилей, теперь не нужно каждый раз обновлять страницу вручную. Общее время сборки, в целом, подтянули.
ng build --optimization=true --build-cache // сборка с кешем
Ведётся работа по улучшению HMR
для шаблонов, уже можно попробовать в бета режиме (шаблоны будут перезагружены, обновив все экземпляры компонентов, без перезагрузки страницы, а состояние приложения сохранится):
NG_HMR_TEMPLATES=1 ng serve
Оставшиеся изменения
Поддержка TypeScript 5.6
standalone: true
теперь по-умолчаниюwithEventReplay()
для SSR теперь стабильнаДобавили связанные сигналы
Заключение
19-я версия содержит в себе действительно много новинок и улучшений, которые улучшают DX. Теперь окончательно ясно, что вектор развития на ближайшие пару лет это SSR, сигналы и standalone-компоненты.
Готовы к обновлению? Тогда вперёд!
ng update @angular/core @angular/cli
От себя рекомендую посмотреть официальное видео, ребята старались и, к тому же, продакшен на высоте:
Написать комментарий