虽然在技术上是正确的,但其他答案将受益于对 Angular 的 URL 到路由匹配的解释。如果您一开始不知道路由器的工作原理,我认为您无法完全(请原谅双关语)理解 pathMatch: full 的作用。
让我们首先定义一些基本的东西。我们将使用此 URL 作为示例:/users/james/articles?from=134#section。
-
这可能很明显,但我们首先要指出,查询参数 (?from=134) 和片段 (#section) 在路径匹配中不起任何作用。只有基本网址 (/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 的唯一子代是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,并且应用程序呈现 nothing。
您可能已经注意到,pathMatch: full 配置基本上是这样说的:
忽略我的孩子,只匹配我。如果我自己无法匹配所有 剩余 URL 段,请继续。
重定向
任何定义了 redirectTo 的 Route 都将根据相同的原则与目标 URL 进行匹配。此处唯一的区别是 只要 segment 匹配 就会应用重定向。这意味着如果重定向路由使用默认的 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',立即应用。
- 目标网址更改为
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: '')
空路径是一种特殊情况,因为它可以匹配 任何 segment 而不会“消耗”它(因此它的孩子必须再次匹配该段) .考虑这个例子:
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 会立即停止(因为我之前提到的保护)并且整个事情运行良好。