虽然在技术上是正确的,其他答案会受益于对Angular的URL到路由匹配进行解释。如果你不知道路由器的工作原理,我认为你无法完全(请原谅双关语)理解
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
对象都是一个节点,可以有子节点,这些子节点又可以有其他子节点或者是叶子节点。
路由器的目标是找到一个与URL的所有部分完全匹配的路由配置分支,从根节点开始。这非常重要!如果Angular找不到能够完全匹配整个URL的路由配置分支,它将不会渲染任何内容。
例如,如果您的目标URL是/a/b/c,但路由器只能匹配/a/b或/a/b/c/d,那么就没有匹配项,应用程序将不会渲染任何内容。
最后,具有redirectTo的路由与常规路由略有不同,我觉得它们可能是唯一真正需要使用pathMatch: full的地方。但我们稍后再讨论这个。
默认(前缀)路径匹配
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
- 段落不匹配,因为带有pathMatch: full
的路径配置users
与完整URLusers/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,
},
],
},
],
}
在这种情况下:
6. `'users' === 'users'` - 该段匹配,但`james/articles`仍未匹配。让我们寻找子路径。
- `permissions' !== 'james'` - 跳过。
- `:userID'`只能匹配一个路径段,即`james`。然而,它是一个`pathMatch: full`的路由,必须匹配整个剩余的URL`james/articles`。它无法做到这一点,因此不匹配(所以我们跳过这个分支)!
7. 再次,我们无法找到URL的任何匹配项,应用程序渲染
nothing。
正如你可能已经注意到的,
pathMatch: full
配置基本上是在说:
忽略我的子路径,只匹配我自己。如果我自己无法完全匹配剩余的URL段,那就继续匹配下一个。
重定向
任何定义了redirectTo
的Route
将根据相同的原则与目标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),以下是会发生的情况:
1. 'not-found' !== 'users' - 跳过它。
2. 'users' === 'users' - 我们有一个匹配。
3. 这个匹配有一个redirectTo: 'not-found',立即应用。
4. 目标URL更改为not-found。
5. 路由器重新开始匹配,并立即找到了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: '**'
将匹配并重定向到欢迎页面。理论上,这应该会触发无限重定向循环,但是Angular会立即停止(因为我之前提到的保护机制),整个过程都能正常运行。
path: ''
) 开始的所有内容。一个空路径 (path: ''
) 将匹配任何段,因此不仅匹配/
,还匹配/welcome
。这意味着当 URL 从/
改变为/welcome
后,path: ''
将再次匹配 : ] 有点奇怪,这是路由系统中的一个特殊情况。我不知道为什么它被设计成这样(从未真正考虑过,但它可能允许方便的组件树),但这正是为什么需要使用pathMatch: 'full'
- 它强制path: ''
精确匹配/
! - Avius