Настройка поведения маршрутов
Angular Router предоставляет мощные точки расширения, позволяющие настраивать поведение маршрутов в приложении. Поведение маршрутизации по умолчанию подходит для большинства приложений, однако для решения конкретных задач часто требуются пользовательские реализации — например, для оптимизации производительности, специализированной обработки URL или сложной логики маршрутизации.
Настройка маршрутов может быть полезна, если приложению требуется:
- Сохранение состояния компонентов при навигации, чтобы избежать повторной загрузки данных
- Стратегическая ленивая загрузка модулей с учетом поведения пользователя или условий сети
- Интеграция с внешними URL или обработка маршрутов Angular вместе с устаревшими системами
- Динамическое сопоставление маршрутов с учетом условий во время выполнения, выходящих за рамки простых шаблонов путей
ПРИМЕЧАНИЕ: Прежде чем реализовывать пользовательские стратегии, убедитесь, что поведение маршрутизатора по умолчанию не отвечает вашим потребностям. Маршрутизация Angular по умолчанию оптимизирована для распространенных сценариев и обеспечивает наилучший баланс производительности и простоты. Настройка стратегий маршрутизации может усложнить код и повлиять на использование памяти, если выполнять ее без должного внимания.
Параметры настройки маршрутизатора
withRouterConfig или RouterModule.forRoot позволяет задавать дополнительные RouterConfigOptions, чтобы изменить поведение Router.
Обработка отмененных переходов
canceledNavigationResolution управляет восстановлением истории браузера маршрутизатором при отмене перехода. По умолчанию используется значение 'replace', которое возвращает URL к состоянию до перехода с помощью location.replaceState. На практике это означает, что если адресная строка уже обновлена в ходе перехода, например при нажатии кнопки «Назад» или «Вперед» в браузере, то при сбое перехода или его отмене защитой запись истории перезаписывается значением для «отката». Переключение на 'computed' синхронизирует индекс текущей записи истории с навигацией Angular, поэтому отмена перехода по кнопке «Назад» запускает переход вперед (и наоборот), возвращая пользователя на исходную страницу.
Эта настройка особенно полезна, если приложение использует urlUpdateStrategy: 'eager' или если защиты часто отменяют popstate-переходы, инициированные браузером.
provideRouter(routes, withRouterConfig({canceledNavigationResolution: 'computed'})); Реакция на переходы по тому же URL
onSameUrlNavigation задает поведение при попытке перейти на текущий URL. Значение по умолчанию 'ignore' пропускает обработку, а 'reload' повторно запускает защиты и резолверы и обновляет экземпляры компонентов.
Это полезно, если повторные нажатия на фильтр списка, элемент боковой навигации или кнопку обновления должны загружать новые данные, хотя URL не изменяется.
provideRouter(routes, withRouterConfig({onSameUrlNavigation: 'reload'}));
Это поведение можно также задавать для отдельных переходов, а не глобально. Так можно сохранить значение по умолчанию 'ignore' и при этом выборочно включать перезагрузку для конкретных сценариев:
router.navigate(['/some-path'], {onSameUrlNavigation: 'reload'}); Управление наследованием параметров
paramsInheritanceStrategy определяет, как параметры маршрута и данные передаются от родительских маршрутов.
По умолчанию ('always') дочерние маршруты автоматически наследуют параметры, данные маршрута и разрешенные значения родительских маршрутов.
provideRouter(routes, withRouterConfig({paramsInheritanceStrategy: 'emptyOnly'}));
export const routes: Routes = [
{
path: 'org/:orgId',
component: Organization,
children: [
{
path: 'projects/:projectId',
component: Project,
children: [
{
path: 'customers/:customerId',
component: Customer,
},
],
},
],
},
];
@Component({
/* ... */
})
export class Customer {
private route = inject(ActivatedRoute);
orgId = this.route.parent?.parent?.snapshot.params['orgId'];
projectId = this.route.parent?.snapshot.params['projectId'];
customerId = this.route.snapshot.params['customerId'];
}
Это обеспечивает доступность матричных параметров, данных маршрута и разрешенных значений на более глубоких уровнях дерева маршрутов — это удобно, когда нужно использовать общие контекстные идентификаторы в разных функциональных областях, например:
/org/:orgId/projects/:projectId/customers/:customerId
@Component({
/* ... */
})
export class Customer {
private route = inject(ActivatedRoute);
// All parent parameters are available directly
orgId = this.route.snapshot.params['orgId'];
projectId = this.route.snapshot.params['projectId'];
customerId = this.route.snapshot.params['customerId'];
} Выбор момента обновления URL
urlUpdateStrategy определяет, когда Angular обновляет адресную строку браузера. Значение по умолчанию 'deferred' ожидает успешного завершения перехода и только после этого меняет URL. Используйте 'eager', чтобы обновлять URL сразу при начале перехода. Немедленное обновление позволяет показать URL, на который выполняется переход, если он завершается ошибкой или блокируется защитой, но при длительной работе защит в адресной строке может ненадолго отображаться URL незавершенного перехода.
Учитывайте это, если системе аналитики необходимо видеть URL, на который выполняется переход, даже когда защиты его блокируют.
provideRouter(routes, withRouterConfig({urlUpdateStrategy: 'eager'})); Выбор способа обработки параметров запроса по умолчанию
defaultQueryParamsHandling задает поведение по умолчанию для Router.createUrlTree, если при вызове не указано queryParamsHandling. Значение по умолчанию — 'replace'; оно заменяет существующую строку запроса. 'merge' объединяет переданные значения с текущими, а 'preserve' сохраняет существующие параметры запроса, если вы явно не укажете новые.
provideRouter(routes, withRouterConfig({defaultQueryParamsHandling: 'merge'}));
Это особенно полезно для страниц поиска и фильтров: существующие фильтры сохраняются автоматически при добавлении новых параметров.
Настройка обработки завершающего слеша
По умолчанию сервис Location удаляет завершающие слеши при чтении URL.
Можно настроить сервис Location так, чтобы он добавлял завершающий слеш ко всем URL, записываемым в браузер. Для этого укажите в приложении TrailingSlashPathLocationStrategy.
import {LocationStrategy, TrailingSlashPathLocationStrategy} from '@angular/common';
bootstrapApplication(App, {
providers: [{provide: LocationStrategy, useClass: TrailingSlashPathLocationStrategy}],
});
Также можно настроить сервис Location так, чтобы он удалял завершающий слеш из всех URL, записываемых в браузер. Для этого укажите в приложении NoTrailingSlashPathLocationStrategy.
import {LocationStrategy, NoTrailingSlashPathLocationStrategy} from '@angular/common';
bootstrapApplication(App, {
providers: [{provide: LocationStrategy, useClass: NoTrailingSlashPathLocationStrategy}],
});
Эти стратегии влияют только на URL, записываемый в браузер. Location.path() и Location.normalize() по-прежнему будут удалять завершающие слеши при чтении URL.
Angular Router предоставляет четыре основных области настройки:
Стратегия повторного использования маршрутов
Стратегия повторного использования маршрутов определяет, уничтожает и создает ли Angular компоненты заново при навигации или сохраняет их для повторного использования. По умолчанию Angular уничтожает экземпляры компонентов при переходе с маршрута и создает новые экземпляры при возвращении на него.
Когда следует реализовать повторное использование маршрутов
Пользовательские стратегии повторного использования маршрутов полезны для приложений, которым требуется:
- Сохранение состояния формы — сохранение частично заполненных форм, когда пользователь уходит со страницы и возвращается
- Сохранение ресурсоемких данных — отказ от повторной загрузки больших наборов данных или выполнения сложных вычислений
- Сохранение позиции прокрутки — сохранение позиции прокрутки в длинных списках или при бесконечной прокрутке
- Интерфейсы с вкладками — сохранение состояния компонентов при переключении между вкладками
Создание пользовательской стратегии повторного использования маршрутов
Класс Angular RouteReuseStrategy позволяет настраивать поведение навигации с помощью концепции «отсоединенных дескрипторов маршрутов».
«Отсоединенные дескрипторы маршрутов» — это способ Angular хранить экземпляры компонентов и всю их иерархию представлений. При отсоединении маршрута Angular сохраняет в памяти экземпляр компонента, его дочерние компоненты и все связанное состояние. Позже это сохраненное состояние можно восстановить при возвращении на маршрут.
Класс RouteReuseStrategy предоставляет следующие методы для управления жизненным циклом компонентов маршрута:
| Метод | Описание |
|---|---|
shouldDetach | Определяет, следует ли сохранить маршрут для последующего повторного использования при переходе с него |
store | Сохраняет дескриптор отсоединенного маршрута, если shouldDetach возвращает true |
shouldAttach | Определяет, следует ли повторно присоединить сохраненный маршрут при переходе на него |
retrieve | Возвращает ранее сохраненный дескриптор маршрута для повторного присоединения |
shouldReuseRoute | Определяет, следует ли маршрутизатору повторно использовать текущий экземпляр маршрута вместо его уничтожения при навигации |
shouldDestroyInjector | (Экспериментально) Определяет, следует ли маршрутизатору уничтожить инжектор отсоединенного маршрута, если он больше не хранится |
В следующем примере показана пользовательская стратегия повторного использования маршрутов, которая выборочно сохраняет состояние компонентов на основе метаданных маршрута:
import {
RouteReuseStrategy,
Route,
ActivatedRouteSnapshot,
DetachedRouteHandle,
} from '@angular/router';
import {Injectable} from '@angular/core';
@Injectable()
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private handlers = new Map<Route | null, DetachedRouteHandle>();
shouldDetach(route: ActivatedRouteSnapshot): boolean {
// Determines if a route should be stored for later reuse
return route.data['reuse'] === true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {
// Stores the detached route handle when shouldDetach returns true
if (handle && route.data['reuse'] === true) {
const key = this.getRouteKey(route);
this.handlers.set(key, handle);
}
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
// Checks if a stored route should be reattached
const key = this.getRouteKey(route);
return route.data['reuse'] === true && this.handlers.has(key);
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
// Returns the stored route handle for reattachment
const key = this.getRouteKey(route);
return route.data['reuse'] === true ? (this.handlers.get(key) ?? null) : null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
// Determines if the router should reuse the current route instance
return future.routeConfig === curr.routeConfig;
}
private getRouteKey(route: ActivatedRouteSnapshot): Route | null {
return route.routeConfig;
}
} Ручное уничтожение дескрипторов отсоединенных маршрутов
При реализации пользовательской RouteReuseStrategy может потребоваться вручную уничтожить DetachedRouteHandle, если вы решили удалить его, не присоединяя повторно. Например, если стратегия ограничивает размер кэша или удаляет дескрипторы по истечении определенного времени, необходимо правильно уничтожить компонент и его состояние, чтобы избежать утечек памяти.
Поскольку DetachedRouteHandle — это непрозрачный тип, напрямую вызвать для него метод уничтожения нельзя. Вместо этого используйте функцию destroyDetachedRouteHandle, предоставляемую Router.
import {destroyDetachedRouteHandle} from '@angular/router';
// ... inside your strategy
if (this.handles.size > MAX_CACHE_SIZE) {
const handle = this.handles.get(oldestKey);
if (handle) {
destroyDetachedRouteHandle(handle);
this.handles.delete(oldestKey);
}
}
ПРИМЕЧАНИЕ: Не используйте путь маршрута в качестве ключа, если задействованы защиты canMatch: это может привести к появлению дублирующихся записей.
(Экспериментально) Автоматическая очистка неиспользуемых инжекторов маршрутов
По умолчанию Angular не уничтожает инжекторы отсоединенных маршрутов, даже если они больше не хранятся в RouteReuseStrategy. Это связано главным образом с тем, что большинству приложений обычно не требуется такое управление памятью.
Чтобы включить автоматическую очистку неиспользуемых инжекторов маршрутов, можно использовать функцию withExperimentalAutoCleanupInjectors в конфигурации маршрутизатора. После переходов эта функция проверяет, какие маршруты в данный момент хранит стратегия, и уничтожает инжекторы отсоединенных маршрутов, которые стратегия повторного использования больше не хранит.
import {provideRouter, withExperimentalAutoCleanupInjectors} from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withExperimentalAutoCleanupInjectors())],
};
Если пользовательская RouteReuseStrategy не задана или ваша пользовательская стратегия наследуется от BaseRouteReuseStrategy, инжекторы будут уничтожаться, когда маршрут неактивен.
Очистка с пользовательской RouteReuseStrategy
Если приложение использует пользовательскую RouteReuseStrategy, и эта стратегия не наследуется от BaseRouteReuseStrategy, необходимо реализовать shouldDestroyInjector, чтобы указать маршрутизатору, у каких маршрутов следует уничтожать инжекторы:
@Injectable()
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
// ... other methods
shouldDestroyInjector(route: Route): boolean {
return !route.data['retainInjector'];
}
}
Если стратегия сохраняет DetachedRouteHandle, также необходимо сообщить о них Router, чтобы он не уничтожил инжекторы, нужные этим отсоединенным дескрипторам:
@Injectable()
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private readonly handles = new Map<Route, DetachedRouteHandle>();
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null) {
this.handles.set(route.routeConfig!, handle);
}
retrieveStoredRouteHandles(): DetachedRouteHandle {
return Array.from(this.handles.values());
}
// ... other methods
} Настройка маршрута для использования пользовательской стратегии повторного использования
Маршруты могут включать поведение повторного использования с помощью метаданных конфигурации маршрута. Такой подход отделяет логику повторного использования от кода компонентов, позволяя легко менять поведение без изменения компонентов:
export const routes: Routes = [
{
path: 'products',
component: ProductList,
data: {reuse: true}, // Component state persists across navigations
},
{
path: 'products/:id',
component: ProductDetail,
// No reuse flag - component recreates on each navigation
},
{
path: 'search',
component: Search,
data: {reuse: true}, // Preserves search results and filter state
},
];
Пользовательскую стратегию повторного использования маршрутов можно также настроить на уровне приложения с помощью системы внедрения зависимостей Angular. В этом случае Angular создает один экземпляр стратегии, управляющий всеми решениями о повторном использовании маршрутов во всем приложении:
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
{provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy},
],
}; Стратегия предварительной загрузки
Стратегии предварительной загрузки определяют, когда Angular загружает модули маршрутов с ленивой загрузкой в фоновом режиме. Ленивая загрузка сокращает время начальной загрузки за счет отложенной загрузки модулей, однако при первом переходе на такой маршрут пользователь все равно испытывает задержку. Стратегии предварительной загрузки устраняют ее, загружая модули до того, как пользователь запросит их.
Встроенные стратегии предварительной загрузки
Angular предоставляет две встроенные стратегии предварительной загрузки:
| Стратегия | Описание |
|---|---|
NoPreloading
| Стратегия по умолчанию, отключающая предварительную загрузку. Иными словами, модули загружаются только при переходе пользователя на соответствующий маршрут |
PreloadAllModules
| Загружает все модули с ленивой загрузкой сразу после начальной навигации |
Стратегию PreloadAllModules можно настроить следующим образом:
import {ApplicationConfig} from '@angular/core';
import {provideRouter, withPreloading, PreloadAllModules} from '@angular/router';
import {routes} from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withPreloading(PreloadAllModules))],
};
Стратегия PreloadAllModules хорошо подходит для небольших и средних приложений, где загрузка всех модулей не оказывает существенного влияния на производительность. Однако для крупных приложений со множеством функциональных модулей может быть полезна более избирательная предварительная загрузка.
Создание пользовательской стратегии предварительной загрузки
Пользовательские стратегии предварительной загрузки реализуют интерфейс PreloadingStrategy, требующий единственного метода preload. Этот метод получает конфигурацию маршрута и функцию, запускающую фактическую загрузку модуля. Стратегия возвращает Observable, который выдает значение по завершении предварительной загрузки, или пустой Observable, если предварительную загрузку следует пропустить:
import {Injectable} from '@angular/core';
import {PreloadingStrategy, Route} from '@angular/router';
import {Observable, of, timer} from 'rxjs';
import {mergeMap} from 'rxjs/operators';
@Injectable()
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
// Only preload routes marked with data: { preload: true }
if (route.data?.['preload']) {
return load();
}
return of(null);
}
}
Эта стратегия выборочной загрузки проверяет метаданные маршрута, чтобы определить поведение предварительной загрузки. Маршруты можно включить в предварительную загрузку с помощью конфигурации:
import {Routes} from '@angular/router';
export const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.routes'),
data: {preload: true}, // Preload immediately after initial navigation
},
{
path: 'reports',
loadChildren: () => import('./reports/reports.routes'),
data: {preload: false}, // Only load when user navigates to reports
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes'),
// No preload flag - won't be preloaded
},
]; Вопросы производительности при предварительной загрузке
Предварительная загрузка влияет как на использование сети, так и на потребление памяти. Каждый предварительно загруженный модуль потребляет трафик и увеличивает объем памяти, занимаемой приложением. Пользователи мобильных устройств с лимитным тарифом могут предпочесть минимальную предварительную загрузку, тогда как настольные устройства с быстрым подключением позволяют использовать более интенсивные стратегии.
Важен и момент предварительной загрузки. Немедленная загрузка после начального запуска может конкурировать за ресурсы с другими важными ресурсами, например изображениями или запросами к API. При выборе стратегии следует учитывать поведение приложения после загрузки и согласовывать ее с другими фоновыми задачами, чтобы не ухудшить производительность.
На предварительную загрузку также влияют ограничения ресурсов браузера. Браузеры ограничивают количество одновременных HTTP-соединений, поэтому интенсивная предварительная загрузка может встать в очередь за другими запросами. Service Worker помогают точнее управлять кэшированием и сетевыми запросами, дополняя стратегию предварительной загрузки.
Стратегия обработки URL
Стратегии обработки URL определяют, какие URL обрабатывает маршрутизатор Angular, а какие игнорирует. По умолчанию Angular пытается обрабатывать все события навигации в приложении, но в реальных приложениях часто требуется сосуществовать с другими системами, обрабатывать внешние ссылки или интегрироваться с устаревшими приложениями, которые управляют собственными маршрутами.
Класс UrlHandlingStrategy позволяет управлять границей между маршрутами, обрабатываемыми Angular, и внешними URL. Это особенно важно при постепенном переносе приложений на Angular или когда приложения Angular должны использовать общее пространство URL с другими платформами.
Реализация пользовательской стратегии обработки URL
Пользовательские стратегии обработки URL наследуются от класса UrlHandlingStrategy и реализуют три метода. Метод shouldProcessUrl определяет, должен ли Angular обрабатывать данный URL, extract возвращает часть URL, которую должен обработать Angular, а merge объединяет фрагмент URL с остальной его частью:
import {Injectable} from '@angular/core';
import {UrlHandlingStrategy, UrlTree} from '@angular/router';
@Injectable()
export class CustomUrlHandlingStrategy implements UrlHandlingStrategy {
shouldProcessUrl(url: UrlTree): boolean {
// Only handle URLs that start with /app or /admin
return url.toString().startsWith('/app') || url.toString().startsWith('/admin');
}
extract(url: UrlTree): UrlTree {
// Return the URL unchanged if we should process it
return url;
}
merge(newUrlPart: UrlTree, rawUrl: UrlTree): UrlTree {
// Combine the URL fragment with the rest of the URL
return newUrlPart;
}
}
Эта стратегия четко разделяет пространство URL. Angular обрабатывает пути /app и /admin, игнорируя все остальные. Такой подход хорошо подходит при переносе устаревших приложений, когда Angular управляет отдельными разделами, а старая система — остальными.
Настройка пользовательской стратегии обработки URL
Пользовательскую стратегию можно зарегистрировать с помощью системы внедрения зависимостей Angular:
import {ApplicationConfig} from '@angular/core';
import {provideRouter} from '@angular/router';
import {UrlHandlingStrategy} from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
{provide: UrlHandlingStrategy, useClass: CustomUrlHandlingStrategy},
],
}; Пользовательские средства сопоставления маршрутов
По умолчанию маршрутизатор Angular перебирает маршруты в заданном порядке и пытается сопоставить путь URL с шаблоном пути каждого маршрута. Поддерживаются статические сегменты, параметризованные сегменты (:id) и подстановочные знаки (**). Используется первый совпавший маршрут, после чего поиск прекращается.
Если приложению требуется более сложная логика сопоставления, основанная на условиях времени выполнения, сложных шаблонах URL или других пользовательских правилах, пользовательские средства сопоставления обеспечивают необходимую гибкость, не усложняя стандартные маршруты.
Маршрутизатор проверяет пользовательские средства сопоставления на этапе сопоставления маршрутов, до проверки соответствия пути. Если средство сопоставления находит совпадение, оно также может извлечь параметры из URL, которые станут доступны активированному компоненту так же, как стандартные параметры маршрута.
Создание пользовательского средства сопоставления
Пользовательское средство сопоставления — это функция, которая получает сегменты URL и возвращает либо результат сопоставления с использованными сегментами и параметрами, либо null, если совпадение не найдено. Функция сопоставления выполняется до того, как Angular проверит свойство path маршрута:
import {Route, UrlSegment, UrlSegmentGroup, UrlMatchResult} from '@angular/router';
export function customMatcher(
segments: UrlSegment[],
group: UrlSegmentGroup,
route: Route,
): UrlMatchResult | null {
// Matching logic here
if (matchSuccessful) {
return {
consumed: segments,
posParams: {
paramName: new UrlSegment('paramValue', {}),
},
};
}
return null;
} Реализация маршрутизации на основе версии
Представьте сайт документации API, которому необходимо выбирать маршрут на основе номера версии в URL. Для разных версий могут использоваться разные структуры компонентов или наборы функций:
import {Routes, UrlSegment, UrlMatchResult} from '@angular/router';
export function versionMatcher(segments: UrlSegment[]): UrlMatchResult | null {
// Match patterns like /v1/docs, /v2.1/docs, /v3.0.1/docs
if (segments.length >= 2 && segments[0].path.match(/^v\d+(\.\d+)*$/)) {
return {
consumed: segments.slice(0, 2), // Consume version and 'docs'
posParams: {
version: segments[0], // Make version available as a parameter
section: segments[1], // Make section available too
},
};
}
return null;
}
// Route configuration
export const routes: Routes = [
{
matcher: versionMatcher,
component: Documentation,
},
{
path: 'latest/docs',
redirectTo: 'v3/docs',
},
];
Компонент получает извлеченные параметры через входные параметры маршрута:
import {Component, input, inject} from '@angular/core';
import {resource} from '@angular/core';
@Component({
selector: 'app-documentation',
template: `
@if (documentation.isLoading()) {
<div>Loading documentation...</div>
} @else if (documentation.error()) {
<div>Error loading documentation</div>
} @else if (documentation.value(); as docs) {
<article>{{ docs.content }}</article>
}
`,
})
export class Documentation {
// Route parameters are automatically bound to signal inputs
version = input.required<string>(); // Receives the version parameter
section = input.required<string>(); // Receives the section parameter
private docsService = inject(DocumentationService);
// Resource automatically loads documentation when version or section changes
documentation = resource({
params: () => {
if (!this.version() || !this.section()) return;
return {
version: this.version(),
section: this.section(),
};
},
loader: ({params}) => {
return this.docsService.loadDocumentation(params.version, params.section);
},
});
} Маршрутизация с учетом локали
В URL международных приложений часто указывается информация о локали. Пользовательское средство сопоставления может извлечь код локали и направить запрос к соответствующему компоненту, предоставив локаль в качестве параметра:
// Supported locales
const locales = ['en', 'es', 'fr', 'de', 'ja', 'zh'];
export function localeMatcher(segments: UrlSegment[]): UrlMatchResult | null {
if (segments.length > 0) {
const potentialLocale = segments[0].path;
if (locales.includes(potentialLocale)) {
// This is a locale prefix, consume it and continue matching
return {
consumed: [segments[0]],
posParams: {
locale: segments[0],
},
};
} else {
// No locale prefix, use default locale
return {
consumed: [], // Don't consume any segments
posParams: {
locale: new UrlSegment('en', {}),
},
};
}
}
return null;
} Сопоставление со сложной бизнес-логикой
Пользовательские средства сопоставления отлично подходят для реализации бизнес-правил, которые неудобно выражать шаблонами путей. Рассмотрим интернет-магазин, где URL товаров имеют разные шаблоны в зависимости от типа товара:
export function productMatcher(segments: UrlSegment[]): UrlMatchResult | null {
if (segments.length === 0) return null;
const firstSegment = segments[0].path;
// Books: /isbn-1234567890
if (firstSegment.startsWith('isbn-')) {
return {
consumed: [segments[0]],
posParams: {
productType: new UrlSegment('book', {}),
identifier: new UrlSegment(firstSegment.substring(5), {}),
},
};
}
// Electronics: /sku/ABC123
if (firstSegment === 'sku' && segments.length > 1) {
return {
consumed: segments.slice(0, 2),
posParams: {
productType: new UrlSegment('electronics', {}),
identifier: segments[1],
},
};
}
// Clothing: /style/BRAND/ITEM
if (firstSegment === 'style' && segments.length > 2) {
return {
consumed: segments.slice(0, 3),
posParams: {
productType: new UrlSegment('clothing', {}),
brand: segments[1],
identifier: segments[2],
},
};
}
return null;
} Вопросы производительности при использовании пользовательских средств сопоставления
Пользовательские средства сопоставления запускаются при каждой попытке навигации, пока не будет найдено совпадение. Поэтому сложная логика сопоставления может замедлить навигацию, особенно в приложениях со множеством маршрутов. Делайте средства сопоставления простыми и эффективными:
- Завершайте работу сразу, если совпадение невозможно
- Избегайте ресурсоемких операций, таких как вызовы API или сложные регулярные выражения
- Рассмотрите возможность кэширования результатов для повторяющихся шаблонов URL
Пользовательские средства сопоставления позволяют элегантно решать сложные задачи маршрутизации, однако их чрезмерное использование затрудняет понимание и поддержку конфигурации маршрутов. Используйте их только в тех случаях, когда стандартное сопоставление путей действительно не подходит.
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/routing/customizing-route-behavior