一、快速上手
- 路由是以模块为单位的,每一个模块都可以有自己的路由。每一个路由对应一个组件。
- 在模块中,需要引入
RouterModule
模块,RouterModule
模块为我们提供了路由功能。并且需要引入并声明所需要展示的组件
...
import {RouterModule} from "@angular/router";
import { HomeComponent } from './pages/home/home.component';
import { AboutComponent } from './pages/about/about.component';
@NgModule({
// 声明当前模块拥有哪些组件
declarations: [
AppComponent, // 根组件
HomeComponent,
AboutComponent,
],
...
})
- 定义路由规则并且声明当前模块依赖路由模块;同时要把路由规则传递给路由模块
RouterModule.forRoot
方法用来定义路由,这个方法确保应用只会实例化一个RouterModule
,第一个参数是一个对象数组,对象通常由两个属性,path
表示地址栏路径,component
表示根据路径动态加载的组件;第二个参数是配置对象,useHash:true
表示使用hash
路由而不是history
路由
...
// path开头不需要加斜线
const routes = [
{path: 'home', component: HomeComponent},
{path: 'about', component: AboutComponent},
];
...
@NgModule({
...
imports: [BrowserModule, RouterModule.forRoot(routes, {useHash: true})],
...
})
- 模块根组件中定义路由跳转链接(必须加斜线)和路由插座(占位符)
routerLink
是一个Angular
的内置指令,该指令将你定义的路由连接到模板文件中。
<a routerLink="/home">首页</a> |
<a routerLink="/about">关于</a>
<router-outlet></router-outlet>
- 点击这两个
a
链接,url
地址和路由插座的位置渲染的内容都会发生相应的变化。
二、匹配规则
(一)重定向
- 地址栏中没有路由地址时重定向到
home
路由 - 默认情况
Angular
在匹配路由规则的时候,匹配的是前缀,path
为空时也就是会匹配前缀为’/'的路由连接,所以要加上pathMatch:'full'
表示路由规则完全匹配
const routes:Routes = [
{path: 'home', component: HomeComponent},
{path: 'about', component: AboutComponent},
{path: '', redirectTo: 'home', pathMatch: 'full'}
];
(二)404页面
- 路由规则的匹配是从上往下依次进行的,当所有路由都没匹配上时,需要展示一个404页面
**
是通配符,会匹配所有路由,所以要放在数组的最后
const routes:Routes = [
{path: 'home', component: HomeComponent},
{path: 'about', component: AboutComponent},
{path: '', redirectTo: 'home', pathMatch: 'full'},
{path: '**', component: NotFoundComponent}
];
三、路由传参
(一)查询参数
- 查询参数在定义路由连接的时候使用
[queryParams]
动态绑定
<a routerLink="/about" [queryParams]="{name:'zs'}">关于</a>
- 在对应的组件被动态渲染的时候,在组件内部就可以获取传递的参数
ActivatedRoute
用于访问动态加载出的组件对应的路由信息route.queryParamMap
是一个Observable
对象,保存查询参数信息;使用get
方法就可以获取对应的值
import {ActivatedRoute} from '@angular/router';
export class AboutComponent {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.queryParamMap.subscribe(query => {
console.log(query.get('name'));
})
}
}
(二)动态参数
- 动态参数需要在路由规则的
path
属性中预留参数的位置
{path: 'about/:name/:age', component: AboutComponent},
- 组件模版中
routerLink
需要绑定动态数组,依次传递路径和参数列表
<a [routerLink]="['/about','zhangsan',78]">关于</a>
- 对象组件中,通过
ActivatedRoute
实例对象的paramMap
获取动态参数信息
四、子级路由
在定义路由的时候,通过路由的children
属性可以定义子级路由。
{
path: 'about',
component: AboutComponent,
children: [
{path: 'company', component: CompanyComponent},
{path: 'industry', component: IndustryComponent},
]
},
在父级页面AboutComponent
中,定义子级路由的跳转链接并且给子级路由定义路由插座
<a routerLink="/about/company">公司</a>
<a routerLink="/about/industry">行业</a>
<router-outlet></router-outlet>
五、导航路由
导航路由可以实现页面跳转到非子级路由的路径上。
<button (click)="goHome()">点击跳转到首页</button>
// 需要引入Router模块
constructor(private router: Router) {}
goHome() {
// 查询参数
this.router.navigate(['/home'],{
queryParams: {
name: 'zhangsan'
}
})
// 动态参数
// this.router.navigate(['/home','zhangsan'])
}
六、路由模块
将定义路由相关的代码抽离出来,放在一个单独的模块中进行管理。
- 在根路径下定义一个模块
ng g m route --flat=true
。--flat=true
不会创建文件夹,直接在当前项目的src/app
下创建相关文件 , 默认值是false
- 将定义路由规则的代码放到模块类中
- 在模块类中定义路由,并且导出
RouterModule
imports: [
CommonModule,
RouterModule.forRoot(routes, {useHash: true})
],
exports: [RouterModule]
- 在AppModule中导入定义的路由模块
imports: [BrowserModule,RouteModule],
七、路由模块懒加载
路由模块懒加载可以实现用户首次请求应用的时候只请求根模块,其他模块等用户访问的时候再加载,优化用户体验
- 创建一个懒加载的模块;创建模块的同时使用
--routing=true
,这样angular-cli
会直接帮我们创建该模块的路由模块
ng g m sports --routing=true
- 创建该模块的根组件
ng g c sports
- 在
sports
路由模块中定义路由;在特性模块中定义路由使用RouterModule.forChild
,只能接受一个路由配置数组作为参数
import {SportsComponent} from "./sports.component";
const routes: Routes = [
{path: '', component: SportsComponent},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
- 在根路由模块的路由规则中定义针对这个特性模块的加载规则
当路由地址是/sport
的时候,动态加载sport
模块
{path: 'sports', loadChildren: () => import('./sports/sports.module').then(m => m.SportsModule)},
- 在根组件模版中定义跳转链接
<a routerLink="/sports">运动界面</a>
通过控制台中的sources
可以看出,当点击了运动界面的按钮之后,会动态加载sports
模块
八、 路由守卫
路由守卫会告诉路由是否允许导航到请求的路由
路由守卫的返回值有四种情况:
- 布尔值:
true
表示可以激活,false
表示不可以激活 UrlTree
:表示一个Url
树,它可以重定向到一个新的Url
- 可以返回异步
Promise<boolean | UrlTree>
:表示一个承诺,它可以解析为布尔值或Url
树 - 可以返回
Observable<boolean | UrlTree>
:表示一个可观察对象,它可以解析为布尔值或Url
树
(一)CanActivate
CanActivate
是一个接口,我们需要自定义一个路由守卫类,路由守卫类要实现这个接口。该接口规定类中必须有canActivate
方法,用来决定是否允许访问路由。一个路由可以应用多个守卫,所有守卫都允许,路由才可以访问。
- 创建一个路由守卫类,放置路由守卫方法
ng g guard guards/authGuard
Angular-cli
会为我们创建一些基础代码,在canActivate
方法中,我们直接返回false
,表示不允许访问目标路由
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree} from '@angular/router';
import {Observable} from 'rxjs';
// 被Injectable修饰,说明路由守卫类是一个服务类
@Injectable({
providedIn: 'root'
})
// AuthGuardGuard:路由守卫类
export class AuthGuardGuard implements CanActivate {
// canActivate:路由守卫方法
// 参数:
// route:ActivatedRouteSnapshot:路由快照,包含与当前组件相关的路由的当前瞬间信息。
// state:RouterStateSnapshot:路由状态快照,表示路由器在当前瞬间的状态
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return false;
}
}
- 在路由规则中给对应路由应用上该路由守卫
{
path: 'user',
loadChildren: () => import('./user/user.module').then(m => m.UserModule),
canActivate: [AuthGuardGuard]
},
(二)CanActivateChild
检测某个路由的子级路由能否被访问
- 创建路由守卫类
ng g guard about/about
,选中CanActivateChild
- 将
canActivateChild
方法的返回值改为false
- 给相应的路由应用这个守卫,这样子就可以实现禁用
about
路由的两个子路由
{
path: 'about',
component: AboutComponent,
children: [
{path: 'company', component: CompanyComponent},
{path: 'industry', component: IndustryComponent}
],
canActivateChild: [AboutGuard]
},
- 点击这两个跳转链接不会发生页面跳转
(三)CanDeactivate
检测用户是否可以离开当前路由,可以用于用户填写表单项,并且还没有保存就要离开的场景。
路由能否离开应该取决于当前应用路由守卫的组件。使用到该路由守卫的组件类中应该定义一个名称相同的方法,用来告诉路由守卫能不能离开当前页面,在路由守卫中调用这个方法来决定返回值是什么。
- 创建路由守卫类
ng g guard unsave/unsave
- 在路由守卫类中,定义一个接口来规定应用到路由守卫的组件中需要定义的判断函数的类型。在
canDeactivate
方法中,根据组件的判断函数,决定返回值;返回true
表示允许离开当前路由,返回false
表示不允许离开。
export interface CanLeave {
canLeave: () => boolean;
}
export class UnsaveGuard implements CanDeactivate<unknown> {
canDeactivate(
component: unknown,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// @ts-ignore
if (component.canLeave()) {
return true;
} else {
if(confirm("是否离开当前页面?")){
return true;
}else{
return false;
}
}
}
}
- 在路由定义的地方给对应路由加上该路由守卫
{
path: 'home/:username',
component: HomeComponent,
canDeactivate: [UnsaveGuard]
},
- 在
HomeComponent
组件类中,需要实现CanLeave
接口,并且定义canleave
方法,返回一个boolean
值,告诉路由守卫当前路由能否离开
export class HomeComponent implements CanLeave{
canLeave(): boolean {
return false;
}
}
- 离开首页时就会弹出弹窗,点击确定可以离开,点击取消阻止路由跳转。
(四)Resolve
跳转路由之前进行异步操作获取数据,在异步操作完成之后才进行路由跳转
- 创建
Resolve
路由守卫类ng g resolver resolvers/username
,Resolve类也是一个服务类 resolve
方法中返回一个Promise
,异步获取并返回所需数据
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<String> {
return new Promise<String>((resolve, reject) => {
setTimeout(() => {
resolve("张三");
}, 2000);
})
}
- 给对应路由应用这个路由守卫,在路由规则定义中增加
resolve
属性,这个属性是一个数组,key
是给组件增加的属性,value
指向获取对应数据的路由守卫
{
path: 'home/:username',
component: HomeComponent,
resolve: {
username: UsernameResolver
}
},
- 在组件中,通过
this.route.snapshot.data
属性就可以获取路由守卫传递过来的数据
constructor(private route: ActivatedRoute) {
}
ngOnInit(): void {
console.log(this.route.snapshot.data['username'])
}
- 点击跳转到首页的按钮,两秒钟之后发生跳转,并且控制台输出