虽然在技术上是正确的,但其他答案将受益于对 Angular 的 URL-to-route 匹配的解释。我不认为你可以完全(原谅双关语)理解pathMatch: full
如果你不知道路由器首先是如何工作的。
让我们首先定义一些基本的东西。我们将使用此 URL 作为示例:/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
!
- 我们可以看到有一个 child
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 会立即停止(因为我之前提到的保护)并且整个事情都很好。