Angular 路由(app-routing)之初体验

339 阅读7分钟

如果你已经具备了简单的Angular CLI和Component基础的话,希望这篇文章能够帮助你理解Angular中的Route对象,还有如何配置达到自己的预期。

当你试图进入Angular官网学习Routes,那丰富的属性只会让你望而却步,越读越懵。不如咱们从最简单的开始,一点点剖析Angular的路由模块吧。

最常见的路由配置

使用route对象的pathcomponent属性,偶尔用到redirectTopatchMatch,或许你就已经能够胜任很多场景下的路由跳转了。 首先我们在新建一个Angular项目时,ng new my-first-router 时,CLI会自动问你是否需要为你添加路由模块,输入y即可

image.png

如果你记忆超群,不如记一下下面的命令行,直接一步到位 ng new my-first-router --routing,这样做的好处就是生成一个app-routing.module.ts的文件,并且自动import到AppModule上。
在开始之前,让我们清空一下app.component.html文件,仅留下最后一行的<router-outlet></router-outlet>

现在打开我们的app-routing.module.ts文件,找到下面这行代码:

const routes: Routes = [];

这将是我们接下来的主战场,下面我们来设想几个场景,我将以本地项目为例子:

  1. 假设我们默认启动的项目URL为localhost:4200,但是我的主页面路径为localhost:4200/home,有什么办法让用户输入localhost:4200就能自动跳转到home路径吗?

    :使用redirectTo,即当路径匹配的时候重定向到redirectTo指定的路径:

const routes: Routes = [{
  path: '',
  redirectTo: 'home',
  pathMatch: 'full'
},
{
  path: 'home',
  component: HomeComponent
}];

解析: 'localhost:4200'后面没有任何字符,所以我们用path:''去匹配,一旦匹配上,就将url改成'localhost:4200/home',随后路径中的home字符串又匹配上了path:'home',因此在AppComponent Html文件中的<router-outlet>标签里将会出现HomeComponent中的内容。(注意:文中提到的component请自行通过ng g component xxx生成,就不重复描述了),请注意上段代码的第四行 patchMatch:'full'这个是必须要的,后面会详细解释,咱们先留个小疑问。(挖坑)

  1. 如果我输入了一个没有匹配上的路径,会发生什么?如何控制它呢?
    :当输入一个不存在的路径时,系统会自动重定向到项目原始路径"localhost:4200",如果想捕捉这类未匹配的路径我们可以巧用通配符path:"**"去匹配一切路径,做一个兜底方案。
const routes: Routes = [{
  path: '',
  redirectTo: 'home',
  pathMatch: 'full'
},
{
  path: 'home',
  component: HomeComponent
},
{
  path: '**',
  component: NotFoundComponent
}
];

试图在浏览器中输入localhost:4200/testPath,你会看到not-found works!的字样,说明NotFoundComponent被加载显示了,path:'**'捕捉到了testPath路径。

image.png

但是使用上面的通配符,切记一点需要把它放在所有的路径配置的最下面,因为Angular是从上往下去匹配路径的,一旦匹配上**就直接去NotFound了,下面配置的所有路径都不再生效啦,切记切记!

有了上面的基础知识,其实一级路径都可以轻松配置啦。但是二级路径又要如何配置呢?

配置子路由 Children属性

children属性其实也是一个Routes对象,所以上面说到的属性也可以重复使用。假设我有一个ParentComponentFirstChildComponentSecondChildComponentParent中有Section A是我无论如何都要显示的,Section B则是根据路由控制的,例如'parent/first-child'路径下,我想在Section B中显示 first-child works!'parent/second-child'路径下我则想显示second child works! 该怎么去配置这个路由呢?

当你想要通过路由控制某一个部位的显示内容的时候,首先在该部位放入<router-outlet></router-outlet>

<!--Section A-->
<p>parent works!</p> 

<!--Section B-->
<router-outlet></router-outlet>

然后在app-routing.module.ts文件中加入下面片段:

...
{ 
  path: 'parent',
  component: ParentComponent,
  children: [
    {
    path: 'first-child',
    component: FirstChildComponent
    },{
    path: 'second-child',
    component: SecondChildComponent  
    }
  ]
},
...

运行结果: image.png

一个简单有效的子路由就实现啦! 关于子路由后面还有懒加载之类的知识,先不在这里多阐述了,有机会再慢慢整理。

详细讲一下pathMatch

当你有了子路由的知识以后,我就可以引入pathMatch的概念了,该属性在Angular官方文档中是这样说的 路径匹配策略,为 “prefix” 或 “full” 之一。默认为“prefix”。再往后的内容无论中英文版本我都读不懂,就不再贴出来了。我开始试图理解这个属性,很明显prefix是前缀的意思,full肯定是它的反之,我结合官方文档中蹩脚的解释猜测,是不是设置成prefix的路径,只要是前半段符合,则自动匹配上;而设置成full的则必须完全匹配; 我试图用localhost:4200/homeTestlocalhost:4200/home/test 去验证我的想法,很不幸我都被带到了not-found页面,说明他们都没有被 {path:'home', pathMatch:'prefix'*默认值*} 捕捉到。

再次研读一下官网信息,有一句话给了我启发例如,如果路由的子项(children)之一与段“user”匹配,则认为“/team/11/user”与前缀“team/:id”匹配。

也就是说team/:id被认为是prefix前缀。当解析/team/11/user时,由于/team/11符合前缀team/:id,因此angular引擎会继续进入该路径的children属性中查找是否有匹配user的子路由,一旦找到,路由即匹配成功。一切似乎变的合理起来。下面只需要写个例子证明这一猜想:
让我们继续使用上面那个子路由的例子,再写一个一级路径parent/first-child,Angular会如何执行呢?

...
//原路径
{ 
  path: 'parent',
  component: ParentComponent,
  children: [
    {
    path: 'first-child',
    component: FirstChildComponent
    },{
    path: 'second-child',
    component: SecondChildComponent  
    }
  ],
  //pathMatch: 'full'
},
// 新增一个路径
{
  path: 'parent/first-child',
  component: FirstChildComponent
},
...

执行结果1: image.png 如果你将path: 'parent'那个Route改成pathMatch:'full'(取消第15行的注释) 试试看呢?
执行结果2: image.png 解析:
首先第一个执行结果,path: 'parent'被捕捉并且执行了。第二个执行结果,path: 'parent/first-child'被执行了。要知道路由是从上往下解析的,一旦有一个规则(path)符合了,就不会再去下面的路径参数中寻找匹配项了。也就是说pathMatch:prefix会让我们的路由匹配器先对路径进行分段式匹配,自动将localhost:4200/parent/first-child 分成 “parent”“first-child”去Routes里面一段段的寻找,最终匹配上了第8行子路由,不会再匹配上第19行这个路径。 但是patchMatch:'full'有种不惯着你这臭毛病的感觉,直接去path里面找,必须找到一模一样路径的parent/first-child才算匹配上。

如果你真正的理解了pathMatch,或许你就能明白为什么下面的路径强调必须要开启full模式了吧?(填坑)

const routes: Routes = [{
  path: '',
  redirectTo: 'home',
  pathMatch: 'full'
},

因为任何的路径我们都可以认为是path:''的子路由,如果还是允许前缀匹配的话,任何路由都会被path:''捕捉再被redirectTo属性带走,谁也别想去自己的路径配置里去。(统统毁灭吧!--发疯文学 :p)

其实pathMatch:'full' 经常跟 redirectTo联合起来用,即你想要的是完全匹配的时候跳走还是前段路径匹配上就跳走。

下面给大家留个思考问题:
如果新加一个下面的route规则,当我在浏览器中访问localhost:4200/bad-path/test的时候,页面应该显示什么内容呢?(有坑!)

...
{
  path: 'bad-path',
  redirectTo: 'redirect-here'
},
{
  path: 'redirect-here',
  component: RedirectHereComponent
},
...

[完整代码](VickySH9112020/my-first-router (github.com))

至此,一个简单的Angular Routes算是解释清楚了,其他的属性我们留到下一篇吧~