Программирование

Динамические маршруты Angular v21 SSR standalone для мобильных/десктоп

Как предоставить динамически загружаемые маршруты в Angular v21 с SSR и standalone-архитектурой для мобильной и десктопной версий на корневом пути ''. Настройка provideRouter, обработка ресайза, lazy loading и лучшие практики роутинга.

5 ответов 1 просмотр

Как в Angular v21 с SSR и standalone-архитектурой предоставить динамически загружаемые маршруты для мобильной и десктопной версий приложения, использующих один корневой путь (‘’)?

В app.ts динамически загружается мобильная или десктопная версия в зависимости от ширины окна:

typescript
protected readonly child = signal<Type<any> | null>(null);
ngOnInit() {
 if (isPlatformBrowser(this.platformId) && window.innerWidth < 768) {
 import('./mobile-version/mobile-version').then(c => this.child.set(c.MobileVersion));
 } else {
 import('./desktop-version/desktop-version').then(c => this.child.set(c.DesktopVersion));
 }
}

Мобильная версия имеет свои дочерние маршруты в mobile-version.routes.ts:

typescript
import { Routes } from '@angular/router';
import { MobileVersion } from './mobile-version';

export const MOBILE_ROUTES: Routes = [
 { path: '', component: MobileVersion },
 { path: 'imprint', loadComponent: () => import('./imprint/imprint').then(c => c.Imprint) },
 { path: 'policy', loadComponent: () => import('./privacy/privacy').then(c => c.Privacy) },
];
  • Как предоставить маршруты мобильной версии при загрузке мобильного компонента?
  • Можно ли определить MOBILE_ROUTES внутри самого MobileVersion или они должны предоставляться из родительского компонента?
  • Продолжит ли роутинг работать корректно при изменении размера окна в процессе использования приложения (если десктопные маршруты загружаются аналогично и используют тот же корневой путь)?
  • Какова лучшая практика для реализации такого сценария?

В Angular v21 с SSR и standalone-архитектурой динамически загружаемые маршруты для мобильной и десктопной версий на одном корневом пути (‘’) предоставляются через provideRouter с функцией, проверяющей ширину окна в bootstrapApplication. Это позволяет избежать багов с ROUTES в lazy-loaded standalone компонентах, обеспечивая корректный angular router при ресайзе. Лучшая практика — определять MOBILE_ROUTES в отдельном файле и предоставлять их условно через higher-level API, с обработчиком ресайза для перезагрузки роутера.


Содержание


Введение в angular router и standalone angular в Angular v21

Angular router в версии 21 идеально вписывается в standalone-архитектуру, где нет модулей, а всё строится на компонентах и провайдерах. Представьте: одно приложение, один корневой путь ‘’, но разные интерфейсы — мобильный и десктопный, загружаемые динамически по ширине экрана. Это не просто трюк, а реальный способ оптимизировать SSR для производительности: сервер рендерит базовый shell, клиент дорисовывает нужный вариант.

В вашем коде сигнал child в app.ts — хороший старт для lazy loading components angular. Но роутинг добавляет сложность: как передать MOBILE_ROUTES в загруженный MobileVersion без перезагрузки страницы? Официальная документация Angular подчёркивает, что router управляет навигацией через outlets и links, обновляя контент без фулл-рилоада. А в standalone всё упрощается provideRouter вместо RouterModule.

Почему это важно именно с angular ssr? Сервер не знает ширину клиента, так что изначально рендерит пустой outlet, а клиент подхватывает. Давайте разберём по шагам, чтобы избежать типичных ловушек вроде “роуты не срабатывают после ресайза”.


Настройка динамической загрузки компонентов с angular ssr

Сначала bootstrapApplication в main.ts. Здесь задаём provideRouter с пустым маршрутом ‘’, где outlet рендерит сигнал child из AppComponent. SSR совместимо, потому что isPlatformBrowser защищает от window на сервере.

Вот базовый setup:

typescript
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app';

bootstrapApplication(AppComponent, {
 providers: [
 provideRouter([
 { path: '', outlet: 'child-outlet', loadComponent: () => import('./app/app').then(c => c.AppComponent) }
 ])
 ]
});

В AppComponent добавьте . Ваш ngOnInit с сигналом child.set(Component) уже загружает MobileVersion или DesktopVersion лениво. Но роуты? Они не передаются автоматически. Angular ROUTES — низкоуровневый токен для DI, но в lazy-load часто глючит.

Альтернатива: сделайте loadComponent функцией, возвращающей нужный компонент по ширине. Но для роутов лучше глобальный провайдер — об этом дальше. На сервере это работает стабильно, клиент гидратирует без мерцания.


Предоставление дочерних маршрутов в angular standalone component

Можно ли определить MOBILE_ROUTES прямо в MobileVersion? Теоретически да, через providers: [ROUTES, MOBILE_ROUTES]. Но на практике в standalone lazy-loaded компонентах это не срабатывает — фабрика не вызывается, роуты игнорируются. Это известный баг, обсуждаемый в GitHub issue Angular.

Правильный путь: предоставлять роуты из родителя или глобально. В app.ts inject Router и используйте setChildRoutes или higher-level API. Лучше — условный provideRouter в bootstrap:

typescript
// Функция для роутов
function getRoutes() {
 if (typeof window !== 'undefined' && window.innerWidth < 768) {
 return MOBILE_ROUTES;
 }
 return DESKTOP_ROUTES; // аналогично
}

// main.ts
provideRouter([{ path: '', children: getRoutes() }]) // Но это статично!

Проблема: getRoutes вызывается раз при bootstrap. Для динамики нужен сервис с сигналом ширины и router.resetConfig. Родительский компонент (App) может inject ROUTER_PROVIDERS и предоставить child роуты через DI.

Коротко: определяйте MOBILE_ROUTES вне компонента (в .routes.ts), импортируйте и предоставляйте через provideRouter в providers AppComponent или bootstrap. Внутри MobileVersion — не рекомендуется из-за бага.


Обработка lazy loading components angular для мобильной и десктопной версий

Lazy loading components angular здесь ключ: import(‘./mobile-version/mobile-version’) — это loadComponent в роуте. Для дочерних: в MOBILE_ROUTES используйте loadComponent для imprint/policy.

В AppComponent шаблон:

html
<ng-container *ngComponentOutlet="child(); selector: ''">
 <router-outlet></router-outlet>
</ng-container>

Selector ‘’ позволяет рендерить child без тега. Роуты загружаются лениво: при навигации на ‘imprint’ подхватывается loadComponent.

С SSR: сервер видит пустой outlet (child null), клиент заполняет. ANGULARarchitects рекомендует именно loadComponent/loadChildren для standalone. Ваш код почти готов — добавьте provideChildRouter в App providers.

Десктоп аналогично: DESKTOP_ROUTES с теми же путями ‘’, ‘imprint’ и т.д. Один корень ‘’ делит роуты nested.


Роутинг при изменении размера окна и angular router navigate

А что при ресайзе? Роутинг сломается: если был мобильный ‘imprint’, а окно расширили — DesktopVersion загрузится, но роуты не обновятся, навигация зависнет.

Решение: слушайте resize в сервисе, обновляйте сигнал child и router.config.

typescript
// route-service.ts
export class RouteService {
 width = signal(window.innerWidth);
 constructor() {
 window.addEventListener('resize', () => this.width.set(window.innerWidth));
 }
}

// app.ts
constructor(private routeService: RouteService, private router: Router) {}
ngOnInit() {
 effect(() => {
 const w = this.routeService.width();
 if (w < 768) {
 import('./mobile-version/mobile-version').then(c => {
 this.child.set(c.MobileVersion);
 this.router.resetConfig(MOBILE_ROUTES); // Или navigate root
 });
 } else {
 // Аналогично для desktop
 }
 });
}

Router.resetConfig(MOBILE_ROUTES) перезагружает роуты. Для angular router navigate используйте { skipLocationChange: true } при смене. Guards (canMatch) проверяют ширину перед активацией роута — идеально для предотвращения багов.

Без этого роутинг “запомнит” старые роуты. Тестируйте: ресайзьте, кликните link — должно перезагружать child роуты.


Лучшие практики и распространенные проблемы в angular lazy load с SSR

Лучшая практика: центральный RouteService с сигналом ширины, provideRouter в bootstrap с динамическими роутами через resolver или factory. Избегайте ROUTES — используйте provideRouter/loadChildren.

Проблемы:

  • Баг с providers в lazy standalone: фикс — higher-level API.
  • SSR hydration mismatch: используйте isPlatformBrowser везде.
  • Bundle size: tree-shake unused роуты.
  • Performance: debounce resize (lodash или RxJS).

Пример полного bootstrap:

typescript
provideRouter([
 {
 path: '',
 loadChildren: () => width < 768 ? import('./mobile.routes') : import('./desktop.routes')
 }
])

Это лениво грузи роуты целиком. Guards для роутов: canMatch(fn(width)).

В проде с angular ssr: ng serve --ssr=false для теста, build с --ssr. Документация подтверждает: nested lazy с standalone — стандарт.

Ресайз без фликера? Signal + ngComponentOutlet магия. Попробуйте — и увидите, как приложение “адаптируется” на лету.


Источники

  1. ROUTES — InjectionToken для низкоуровневой конфигурации angular router в standalone: https://angular.dev/api/router/ROUTES
  2. Routing — Руководство по роутингу, outlets и lazy loading в Angular: https://angular.dev/guide/routing
  3. Routing and Lazy-Loading with Standalone Components — Практика роутинга и lazy load в standalone angular: https://www.angulararchitects.io/en/blog/routing-and-lazy-loading-with-standalone-components/
  4. Standalone lazy-loaded ROUTES providers not working — Обсуждение бага с ROUTES в lazy компонентах: https://github.com/angular/angular/issues/54518

Заключение

В Angular v21 с SSR динамические роуты для мобильной/десктопной версий на ‘’ реализуются через условный provideRouter и RouteService с сигналом ширины — это решает все под вопросы: предоставление MOBILE_ROUTES извне, ресайз без сбоев и lazy loading. Забудьте ROUTES из-за багов, полагайтесь на loadComponent и guards. В итоге приложение лёгкое, адаптивное и SSR-ready. Начните с сервиса — и роутинг заработает как часы, даже при безумных ресайзах.

A

ROUTES — это InjectionToken для низкоуровневой конфигурации angular router через dependency injection в standalone angular. Рекомендуется использовать высокоуровневые API, такие как provideRouter или RouterModule.forRoot(), вместо прямого ROUTES для настройки маршрутов с angular ssr. Это упрощает lazy loading components angular и обеспечивает совместимость с SSR в Angular v21.

  • Подходит для динамических child-маршрутов в мобильных/десктопных версиях.
  • Избегайте прямого использования из-за потенциальных багов в lazy-loaded компонентах.
A

Angular Router управляет навигацией в SPA, определяя компоненты по URL с использованием outlets для рендеринга и links для angular router navigate без перезагрузки страницы. Поддерживает nested routes, guards, params и lazy loading в standalone angular. В angular ssr роутинг обновляет контент на клиенте после гидратации index.html, идеально для динамических мобильных/десктопных версий на одном корневом пути ‘’.

Manfred Steyer / Google Developer Expert

В standalone angular используйте provideRouter с loadComponent или loadChildren для angular lazy load маршрутов. Для динамических мобильных/десктопных версий создайте функцию, проверяющую window.innerWidthisPlatformBrowser для angular ssr), и передайте условные MOBILE_ROUTES в bootstrapApplication. Маршруты можно определять внутри MobileVersion или импортировать; при ресайзе обновляйте Router с guards для корректного angular router navigate.

  • Лучшая практика: сервис для детекции размера + динамические провайдеры.
@ketec / Разработчик

В standalone lazy-loaded компонентах провайдер ROUTES не срабатывает: фабрика не вызывается, маршруты не предоставляются, даже с inject() в loadChildren. Это известный баг в angular router (Angular 15+), где ROUTES в providers компонента или маршрутов игнорируется при lazy loading components angular. Рекомендуется использовать higher-level API вроде provideRouter для динамических child-маршрутов в standalone angular с SSR.

Авторы
A
Команда разработчиков
Manfred Steyer / Google Developer Expert
Google Developer Expert
@ketec / Разработчик
Разработчик
Источники
Angular / Документация фреймворка
Документация фреймворка
ANGULARarchitects / Платформа для обучения Angular
Платформа для обучения Angular
GitHub / Платформа хостинга исходного кода
Платформа хостинга исходного кода
Проверено модерацией
НейроОтветы
Модерация
Динамические маршруты Angular v21 SSR standalone для мобильных/десктоп