Хотя технически правильно, другие ответы выиграют от объяснения соответствия URL-адреса Angular и маршрута. Я не думаю, что вы можете полностью (простите за каламбур) понять, что pathMatch: full
делает, если вы вообще не знаете, как работает маршрутизатор.
Давайте сначала определим несколько основных вещей. Мы будем использовать этот адрес в качестве примера: /users/james/articles?from=134#section
.
Это может быть очевидно, но сначала отметим, что параметры запроса ( ?from=134
) и фрагменты ( #section
) не играют никакой роли в сопоставлении путей . Имеет значение только базовый url ( /users/james/articles
).
Angular разбивает URL-адреса на сегменты . Сегменты - /users/james/articles
это, конечно users
, james
и articles
.
Конфигурация маршрутизатора представляет собой древовидную структуру с одним корневым узлом. Каждый Route
объект представляет собой узел, который может иметь children
узлы, которые, в свою очередь, могут иметь другие children
или быть листовыми узлами.
Цель маршрутизатора - найти ветвь конфигурации маршрутизатора , начинающуюся с корневого узла, которая соответствовала бы точно всем (!!!) сегментам URL. Это очень важно! Если Angular не находит ветку конфигурации маршрута, которая могла бы соответствовать всему URL-адресу - ни больше ни меньше - он ничего не отобразит .
Например, если ваш целевой URL-адрес, /a/b/c
но маршрутизатор может сопоставить только либо /a/b
или /a/b/c/d
, то совпадения нет, и приложение ничего не отобразит.
Наконец, маршруты с redirectTo
ведут себя немного иначе, чем обычные маршруты, и мне кажется, что они будут единственным местом, где кто-то действительно когда-либо захочет воспользоваться pathMatch: full
. Но мы вернемся к этому позже.
Соответствие prefix
пути по умолчанию ( )
Причина названия prefix
заключается в том, что такая конфигурация маршрута будет проверять, является ли сконфигурированный path
префиксом оставшихся сегментов URL. Однако маршрутизатор может сопоставлять только полные сегменты , что немного сбивает с толку это наименование.
В любом случае, допустим, это наша конфигурация маршрутизатора корневого уровня:
const routes: Routes = [
{
path: 'products',
children: [
{
path: ':productID',
component: ProductComponent,
},
],
},
{
path: ':other',
children: [
{
path: 'tricks',
component: TricksComponent,
},
],
},
{
path: 'user',
component: UsersonComponent,
},
{
path: 'users',
children: [
{
path: 'permissions',
component: UsersPermissionsComponent,
},
{
path: ':userID',
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
},
],
},
];
Обратите внимание, что каждый отдельный Route
объект здесь использует стратегию сопоставления по умолчанию, то есть prefix
. Эта стратегия означает, что маршрутизатор выполняет итерацию по всему дереву конфигурации и пытается сопоставить его с целевым URL-адресом сегмент за сегментом, пока URL-адрес не будет полностью сопоставлен . Вот как это будет сделано для этого примера:
- Обходите корневой массив в поисках точного совпадения для первого сегмента URL -
users
.
'products' !== 'users'
, так что пропустите эту ветку. Обратите внимание, что мы используем проверку на равенство, а не .startsWith()
или .includes()
- учитываются только совпадения полного сегмента!
:other
соответствует любому значению, так что это совпадение. Однако целевой URL еще не полностью сопоставлен (нам все еще нужно сопоставить james
и articles
), поэтому маршрутизатор ищет дочерние элементы.
- Единственный ребенок
:other
IS tricks
, который !== 'james'
, следовательно , не совпадают.
- Затем Angular возвращается к корневому массиву и продолжает оттуда.
'user' !== 'users
, пропустить ветку.
'users' === 'users
- сегмент совпадает. Однако это еще не полное совпадение, поэтому нам нужно искать детей (как в шаге 3).
'permissions' !== 'james'
, пропустить это.
:userID
соответствует чему угодно, таким образом, мы имеем совпадение для james
сегмента. Однако это еще не полное совпадение, поэтому нам нужно искать дочерний элемент, который бы совпадал articles
.
- Мы видим, что у
:userID
него есть дочерний маршрут articles
, который дает нам полное совпадение! Таким образом приложение выполняет рендеринг UserArticlesComponent
.
Соответствие полного URL ( full
)
Пример 1
Теперь представьте, что users
объект конфигурации маршрута выглядел так:
{
path: 'users',
component: UsersComponent,
pathMatch: 'full',
children: [
{
path: 'permissions',
component: UsersPermissionsComponent,
},
{
path: ':userID',
component: UserComponent,
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
},
],
}
Обратите внимание на использование pathMatch: full
. Если бы это было так, шаги 1-5 были бы такими же, но шаг 6 был бы другим:
'users' !== 'users/james/articles
- сегмент не совпадает, потому что конфигурация пути users
с pathMatch: full
не соответствует полному URL-адресу, а именно users/james/articles
.
- Так как совпадений нет, мы пропускаем эту ветку.
- На этом этапе мы достигли конца настройки маршрутизатора, не найдя совпадения. Приложение ничего не отображает .
Пример 2
Что, если бы у нас было это:
{
path: 'users/:userID',
component: UsersComponent,
pathMatch: 'full',
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
}
users/:userID
pathMatch: full
только с совпадениями, users/james
поэтому снова нет совпадений, и приложение ничего не отображает.
Пример 3
Давайте рассмотрим это:
{
path: 'users',
children: [
{
path: 'permissions',
component: UsersPermissionsComponent,
},
{
path: ':userID',
component: UserComponent,
pathMatch: 'full',
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
},
],
}
В этом случае:
'users' === 'users
- сегмент совпадает, но по- james/articles
прежнему не совпадает . Поищем детей.
'permissions' !== 'james'
- пропускать.
:userID'
может соответствовать только одному сегменту, который был бы james
. Однако это pathMatch: full
маршрут, и он должен совпадать james/articles
(весь оставшийся URL). Он не может этого сделать, и поэтому это не совпадение (поэтому мы пропускаем эту ветку)!
- Опять же, нам не удалось найти совпадения для URL-адреса, и приложение ничего не отображает .
Как вы могли заметить, pathMatch: full
конфигурация в основном говорит следующее:
Не обращай внимания на моих детей и сравнивайся только со мной. Если я не могу самостоятельно сопоставить все оставшиеся сегменты URL, продолжайте.
Перенаправления
Любой Route
объект, для которого задан a, redirectTo
будет сопоставлен с целевым URL-адресом в соответствии с теми же принципами. Единственная разница в том, что перенаправление применяется, как только сегмент совпадает . Это означает, что если маршрут перенаправления использует prefix
стратегию по умолчанию , частичного совпадения достаточно, чтобы вызвать перенаправление . Вот хороший пример:
const routes: Routes = [
{
path: 'not-found',
component: NotFoundComponent,
},
{
path: 'users',
redirectTo: 'not-found',
},
{
path: 'users/:userID',
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
},
];
Для нашего исходного URL ( /users/james/articles
) вот что произойдет:
'not-found' !== 'users'
- пропустить это.
'users' === 'users'
- у нас есть матч.
- У этого совпадения есть
redirectTo: 'not-found'
, который применяется немедленно .
- Целевой URL изменится на
not-found
.
- Маршрутизатор снова начинает сопоставление и сразу находит совпадение
not-found
. Приложение отображает NotFoundComponent
.
Теперь подумайте, что бы произошло, если бы в users
маршруте также были pathMatch: full
:
const routes: Routes = [
{
path: 'not-found',
component: NotFoundComponent,
},
{
path: 'users',
pathMatch: 'full',
redirectTo: 'not-found',
},
{
path: 'users/:userID',
children: [
{
path: 'comments',
component: UserCommentsComponent,
},
{
path: 'articles',
component: UserArticlesComponent,
},
],
},
];
'not-found' !== 'users'
- пропустить это.
users
будет соответствовать первому сегменту URL-адреса, но конфигурация маршрута требует full
совпадения, поэтому пропустите его.
'users/:userID'
совпадения users/james
. articles
все еще не найден, но у этого маршрута есть дочерние элементы.
- Мы находим совпадение
articles
в детях. Теперь соответствует весь URL-адрес, и приложение выполняет рендеринг UserArticlesComponent
.
Пустой путь ( path: ''
)
Пустой путь - это особый случай, потому что он может соответствовать любому сегменту, не «потребляя» его (так что дочерним элементам придется снова сопоставить этот сегмент). Рассмотрим этот пример:
const routes: Routes = [
{
path: '',
children: [
{
path: 'users',
component: BadUsersComponent,
}
]
},
{
path: 'users',
component: GoodUsersComponent,
},
];
Допустим, мы пытаемся получить доступ /users
:
path: ''
всегда будет совпадать, следовательно, маршрут совпадает. Однако весь URL-адрес не был сопоставлен - нам все еще нужно сопоставить users
!
- Мы видим, что есть дочерний элемент
users
, который соответствует оставшемуся (и единственному!) Сегменту, и у нас есть полное совпадение. Приложение отображает BadUsersComponent
.
Теперь вернемся к исходному вопросу
OP использовал эту конфигурацию маршрутизатора:
const routes: Routes = [
{
path: 'welcome',
component: WelcomeComponent,
},
{
path: '',
redirectTo: 'welcome',
pathMatch: 'full',
},
{
path: '**',
redirectTo: 'welcome',
pathMatch: 'full',
},
];
Если мы переходим к корневому URL ( /
), вот как маршрутизатор решит это:
welcome
не соответствует пустому сегменту, поэтому пропустите его.
path: ''
соответствует пустому сегменту. У него есть pathMatch: 'full'
, что также удовлетворяет, поскольку мы сопоставили весь URL (у него был единственный пустой сегмент).
welcome
Происходит перенаправление на , и приложение выполняет рендеринг WelcomeComponent
.
Что, если не было pathMatch: 'full'
?
Собственно, можно было бы ожидать, что все будет вести себя точно так же. Однако Angular явно запрещает такую конфигурацию ( { path: '', redirectTo: 'welcome' }
), потому что, если вы поместите это Route
выше welcome
, теоретически это создаст бесконечный цикл перенаправлений. Итак, Angular просто выдает ошибку , поэтому приложение вообще не работает! ( https://angular.io/api/router/Route#pathMatch )
На самом деле, для меня это не имеет особого смысла, потому что Angular также реализовал защиту от таких бесконечных перенаправлений - он выполняет только одно перенаправление для каждого уровня маршрутизации! Это остановит все дальнейшие перенаправления (как вы увидите в примере ниже).
О чем path: '**'
?
path: '**'
будет соответствовать абсолютно чему угодно ( af/frewf/321532152/fsa
является совпадением) с или без pathMatch: 'full'
.
Кроме того, поскольку он соответствует всему, корневой путь также включен, что делает { path: '', redirectTo: 'welcome' }
эту настройку полностью избыточной.
Как ни странно, вполне нормально иметь такую конфигурацию:
const routes: Routes = [
{
path: '**',
redirectTo: 'welcome'
},
{
path: 'welcome',
component: WelcomeComponent,
},
];
Если мы перейдем к /welcome
, path: '**'
будет совпадение и произойдет перенаправление на welcome. Теоретически это должно запустить бесконечный цикл перенаправлений, но Angular немедленно останавливает это (из-за защиты, о которой я упоминал ранее), и все это работает нормально.