在Angular中,“pathmatch:full”是什么,它有什么影响?

187

这里使用 pathmatch 作为完整匹配,当我删除这个 pathmatch 时,应用程序甚至无法加载或运行该项目。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { AppComponent }  from './app.component';
import { WelcomeComponent } from './home/welcome.component';

/* Feature Modules */
import { ProductModule } from './products/product.module';

@NgModule({
  imports: [
    BrowserModule,
    HttpModule,
    RouterModule.forRoot([
      { path: 'welcome', component: WelcomeComponent },
      { path: '', redirectTo: 'welcome', pathMatch: 'full' },
      { path: '**', redirectTo: 'welcome', pathMatch: 'full' }
    ]),
    ProductModule
  ],
  declarations: [
    AppComponent,
    WelcomeComponent
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
5个回答

208
虽然在技术上是正确的,其他答案会受益于对Angular的URL到路由匹配进行解释。如果你不知道路由器的工作原理,我认为你无法完全(请原谅双关语)理解pathMatch: full的作用。
首先,让我们定义一些基本概念。我们将以此URL作为示例:/users/james/articles?from=134#section
  1. 可能很明显,但首先要指出的是查询参数(?from=134)和片段(#section)在路径匹配中不起任何作用。只有基本URL(/users/james/articles)才重要。

  2. Angular将URL拆分成。当然,/users/james/articles的段是usersjamesarticles

  3. 路由配置是一个带有单个根节点的树形结构。每个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完全匹配为止。以下是针对此示例的操作步骤:
  1. 遍历根数组,寻找第一个URL段的精确匹配 - users
  2. 'products' !== 'users',因此跳过该分支。请注意,我们使用的是相等性检查,而不是.startsWith().includes() - 只有完全匹配的段才计数!
  3. :other匹配任何值,因此它是一个匹配项。但是,目标URL还没有完全匹配(我们仍然需要匹配jamesarticles),因此路由器会寻找子级。
  • :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步将会有所不同:
  1. 'users' !== 'users/james/articles - 段落不匹配,因为带有pathMatch: full的路径配置users与完整URLusers/james/articles不匹配。
  2. 由于没有匹配项,我们跳过了这个分支。
  3. 到达路由器配置的末尾时,没有找到匹配项。应用程序不渲染任何内容。

示例2

如果我们有以下情况:

{
  path: 'users/:userID',
  component: UsersComponent,
  pathMatch: 'full',
  children: [
    {
      path: 'comments',
      component: UserCommentsComponent,
    },
    {
      path: 'articles',
      component: UserArticlesComponent,
    },
  ],
}

users/:userIDpathMatch: 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段,那就继续匹配下一个。

重定向

任何定义了redirectToRoute将根据相同的原则与目标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,
      },
    ],
  },
];
  1. 'not-found' !== 'users' - 跳过它。
  2. users 会匹配 URL 的第一个片段,但路由配置要求进行 full 匹配,因此跳过它。
  3. '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(/),路由器将如何解析它:
  1. welcome不匹配空段,所以跳过它。
  2. path: ''匹配空段。它有一个pathMatch: 'full',因为我们已经匹配了整个URL(它只有一个空段)。
  3. 重定向到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,
  },
];

如果我们导航到/welcomepath: '**'将匹配并重定向到欢迎页面。理论上,这应该会触发无限重定向循环,但是Angular会立即停止(因为我之前提到的保护机制),整个过程都能正常运行。

我在“What if there was no pathMatch: 'full'?”这一部分有些迷惑。如果“/”匹配了“{ path: '', redirectTo: 'welcome' }”,并且重定向到“welcome”并同时将URL更改为“welcome”,为什么会创建一个无限循环呢?为什么不会渲染WelcomeComponent? - Hagalaz
你可能需要重新阅读从 空路径 (path: '') 开始的所有内容。一个空路径 (path: '') 将匹配任何段,因此不仅匹配 /,还匹配 /welcome。这意味着当 URL 从 / 改变为 /welcome 后,path: '' 将再次匹配 : ] 有点奇怪,这是路由系统中的一个特殊情况。我不知道为什么它被设计成这样(从未真正考虑过,但它可能允许方便的组件树),但这正是为什么需要使用 pathMatch: 'full' - 它强制 path: '' 精确匹配 / - Avius
10
太棒了!很少有人会深入到这样的程度,涵盖问题中如此多的方面。非常详细的回答。谢谢! - Anmol Saraf
5
我见过的关于路由的最佳解释;-) - Don
1
这不仅是我见过的最好的路由解释,也是我见过的任何IT解释中最好的之一 :-) - YoNuevo
显示剩余2条评论

155
RouterModule.forRoot([
      { path: 'welcome', component: WelcomeComponent },
      { path: '', redirectTo: 'welcome', pathMatch: 'full' },
      { path: '**', component: 'pageNotFoundComponent' }
    ])

情况 1 pathMatch:'full': 在此情况下,当应用程序启动在localhost:4200(或某个服务器)时,默认页面将是欢迎屏幕,因为URL将是https://localhost:4200/

如果https://localhost:4200/gibberish,由于path:'**'通配符,这将重定向到pageNotFound屏幕

情况 2 pathMatch:'prefix'

如果路由有{ path: '', redirectTo: 'welcome', pathMatch: 'prefix' },现在它将永远不会到达通配符路由,因为每个URL都与定义的path:''匹配。


1
你好,感谢您对这个例子进行清晰的解释。但是,您能否给我们提供另一个类型的路由示例,以使其完全清晰明了?(例如使用子路由等示例)。谢谢。 - sohaieb azaiez
非常好的解释,但您能告诉我如何配置两个不同的布局,例如内部布局和外部布局吗? - Kapil Soni
我无法重现“Case 2”。这里有一个示例,我期望第一个路由匹配所有内容,但事实并非如此。我提出了这个问题以尝试获得解释。 - Olivier Boissé

105

pathMatch='full'表示当URL的剩余未匹配段与前缀路径匹配时,路由命中。

pathMatch='prefix'告诉路由器,当剩余的URL以重定向路由的前缀路径开头时,匹配重定向路由。

参考:https://angular.io/guide/router#set-up-redirects

pathMatch: 'full'表示整个URL路径需要匹配,并由路由匹配算法消耗。

pathMatch: 'prefix'表示选择路径与URL开头匹配的第一个路由,然后路由匹配算法继续搜索匹配子路由的其余URL部分。


10

路径匹配策略,可选值为'prefix'或'full'。默认值为'prefix'。

默认情况下,路由器从左边开始检查URL元素以确定URL是否与给定路径匹配,并在找到匹配项后停止。例如,'/team/11/user' 与 'team/:id' 匹配。

当重定向空路径路由时,路径匹配策略'full'将针对整个URL进行匹配非常重要。否则,因为空路径是任何URL的前缀,即使在导航到重定向目标时,路由器也会应用重定向并创建无限循环。

来源:https://angular.io/api/router/Route#properties


2
在我看来,这个解释(在给出的解释中)是最简单和最容易理解的。 - YulePale

0

Angular的默认行为是:所有路由都使用{pathMatch:'prefix'}。

现在,让我们看看这两者之间的区别:

如果pathMatch: 'prefix' => Angular将会在路由数组中搜索URL的路径前缀。

如果pathMatch: 'full' => Angular将会在路由数组中搜索URL的完整路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接