【Angular】路由
在根模块中通过 RouterModule.forRoot()
方法来创建单例路由器
在根组件中引入 <router-outlet></router-outlet>
指明路由填充的位置
路由抽离为独立模块
把路由配置写在 pages.routing.module.ts
文件中
此文件作为根路由模块导入到 app.module
RouterModule.forRoot
用来创建全局唯一的根路由实例
- 根路由模块
pages.routing.module.ts
import { RouterModule } from "@angular/router";
import { NgModule } from "@angular/core";
import { PageAComponent } from "./page-a/page-a.component";
import { PageBComponent } from "./page-b/page-b.component";
import { PageCComponent } from "./page-c/page-c.component";
@NgModule({
imports: [
RouterModule.forRoot([
{
path: "a",
component: PageAComponent,
children: [
{
path: "c",
component: PageCComponent,
},
],
},
{
path: "b",
component: PageBComponent,
},
{
path: "**",
redirectTo: "/a",
pathMatch: "full",
},
]),
],
})
export class PagesRoutingModule {}
异步路由(懒加载)及导入其他子路由模块
每一个功能模块都有自己的 module.ts
文件 及 routing.ts
文件,把每个功能模块的路由及依赖都各自管理,最终只需要在上级模块中引入这个功能模块的 module.ts
即可
- 根路由模块
pages/pages.routing.module.ts
import { RouterModule } from "@angular/router";
import { NgModule } from "@angular/core";
import { HomeComponent } from "./home/home.component";
@NgModule({
imports: [
RouterModule.forRoot([
{
path: "home",
component: HomeComponent, // home是一个普通组件,内部有 <router-outlet></router-outlet> 标签做嵌套路由
children: [
{
path: "",
loadChildren: () =>
import("./page-module-a/page-module-a.module").then(
(m) => m.PageModuleAModule
), // 懒加载路由
},
],
},
{
path: "**",
redirectTo: "/home", // 所有未定义的路由都跳转到home
pathMatch: "full",
},
]),
],
})
export class PagesRoutingModule {}
- 子功能模块
pages/page-module-a/page-module-a.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { PageBComponent } from "./page-b/page-b.component";
import { PageAComponent } from "./page-a/page-a.component";
import { PageModuleARoutingModule } from "./page-module-a.routing.module";
@NgModule({
declarations: [PageBComponent, PageAComponent],
imports: [CommonModule, PageModuleARoutingModule],
})
export class PageModuleAModule {}
- 子路由模块
pages/page-module-a/page-module-a.routing.module.ts
import { NgModule } from "@angular/core";
import { PageBComponent } from "./page-b/page-b.component";
import { PageAComponent } from "./page-a/page-a.component";
import { RouterModule, Routes } from "@angular/router";
const routes: Routes = [
{
path: "module-a-page-a",
component: PageAComponent,
},
{
path: "module-a-page-b",
component: PageBComponent,
},
];
@NgModule({
declarations: [],
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class PageModuleARoutingModule {}
带参路由
将 path
设置为 args-page/:id
来定义一个带参路由, id
对于这个路由来说是必须的
在带参路由指定的组件中通过 ActivatedRoute
来获取路由参数
- 路由模块
pages/pages-routing.module.ts
import { RouterModule } from "@angular/router";
import { NgModule } from "@angular/core";
import { ArgsPageComponent } from "./args-page/args-page.component";
@NgModule({
imports: [
RouterModule.forRoot([
{
path: "args-page/:id", // id对于此路由来说是必须的,只有形如 args-page/12 这样的url才会匹配
component: ArgsPageComponent,
},
]),
],
})
export class PagesRoutingModule {}
- 组件
pages/args-page/args-page.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-args-page',
templateUrl: './args-page.component.html',
styleUrls: ['./args-page.component.css']
})
export class ArgsPageComponent implements OnInit {
constructor(
private route: ActivatedRoute
) { }
id: string;
ngOnInit(): void {
this.route.params.subscribe(params => {
this.id = params.id;
})
}
}
路由守卫
守卫返回一个值,以控制路由器的行为:
如果它返回
true
,导航过程会继续如果它返回
false
,导航过程就会终止,且用户留在原地。如果它返回
UrlTree
,则取消当前的导航,并且开始导航到返回的这个UrlTree
。
在分层路由的每个级别上,你都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查 CanDeactivate()
和 CanActivateChild()
守卫。 然后它会按照从上到下的顺序检查 CanActivate()
守卫。 如果特性模块是异步加载的,在加载它之前还会检查 CanLoad()
守卫。 如果任何一个守卫返回 false
,其它尚未完成的守卫会被取消,这样整个导航就被取消了。
类型
可以在一个文件中定义所有守卫,路由只会根据其引用类型执行对应的守卫
canActivate
会在任何路由被激活之前触发。canActivateChild
在任何子路由被激活之前触发。注意该守卫需要定义在父路由上守卫其子路由。canDeactivate
离开路由前触发。resolve
在路由激活之前获取路由数据。在该方法中异步获取数据,可以返回一个
Promise
或一个Observable
防止组件在获取数据前被渲染,或者直接返回一个值来支持同步方式。canLoad
来处理异步导航到某特性模块的情况。 如果一个异步路由(懒加载)无权访问,即使在其他守卫中返回false
阻止访问却依然会加载该异步路由的模块,使用canLoad
可以在无权访问时不加载该模块
执行顺序
- 异步路由时执行目标路由的
canLoad
- 从上一个路由离开
canDeactivate
- 当前子路由守卫
CanActivateChild
- 当前路由守卫
canActivate
定义及使用
canDeactivate
、CanActivateChild
、canActivate
、canLoad
只需要引入即可
resolve
用于在路由激活之前获取数据,在守卫文件中定义 resolve
方法并返回数据后需要在对应的组件文件中接收数据
- 路由守卫文件
auth/auth.guard.ts
import { Injectable } from "@angular/core";
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
UrlTree,
} from "@angular/router";
@Injectable({
providedIn: "root",
})
export class AuthGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
console.log("AuthGuard", state.url);
return true;
}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
console.log("ChildAuthGuard", state.url);
return true;
}
canDeactivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
console.log("canDeactivate", state.url);
return true;
}
canLoad() {
return true;
}
}
- 路由模块
pages/pages-routing.module.ts
import { RouterModule } from "@angular/router";
import { NgModule } from "@angular/core";
import { HomeComponent } from "./home/home.component";
import { ArgsPageComponent } from "./args-page/args-page.component";
import { AuthGuard } from "../auth/auth.guard";
@NgModule({
imports: [
RouterModule.forRoot([
{
path: "args-page/:id",
component: ArgsPageComponent,
canActivate: [AuthGuard], // 跳转当前路由时触发
canDeactivate: [AuthGuard], // 离开当前路由时触发
},
{
path: "home",
component: HomeComponent,
canActivate: [AuthGuard],
canActivateChild: [AuthGuard], // 当前子路由变化时触发
resolve: [AuthGuard], // 接收时数据为数组形式
// resolve: { // 接收时数据为对象形式
// heroData: AuthGuard
// },
children: [
{
path: "",
canLoad: [AuthGuard],
loadChildren: () =>
import("./page-module-a/page-module-a.module").then(
(m) => m.PageModuleAModule
),
},
],
},
{
path: "**",
redirectTo: "/home", // 所有未定义的路由都跳转到home
pathMatch: "full",
},
]),
],
})
export class PagesRoutingModule {}
- 接收
resolve
返回的数据,pages/home/home.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(
private route: ActivatedRoute
) { }
ngOnInit(): void {
this.route.data.subscribe(data => {
console.log('home---data', data); // 在定义路由时如果resolve守卫使用的是数组形式则data为数组,用对象形式则为对象。
})
}
}
预加载
只要有 canLoad
守卫就不会被预加载
自定义预加载策略
这里配置的策略为:data.preload
为 true
时预加载
- 路由模块
pages/pages-routing.module.ts
import { RouterModule } from "@angular/router";
import { NgModule } from "@angular/core";
import { HomeComponent } from "./home/home.component";
import { AuthGuard } from "../auth/auth.guard";
import { SelectivePreloadingStrategyService } from "./selective-preloading-strategy.service";
@NgModule({
imports: [
RouterModule.forRoot(
[
{
path: "home",
component: HomeComponent,
children: [
{
path: "",
// canLoad: [AuthGuard], // 只要有canLoad守卫就不会被预加载
loadChildren: () =>
import("./page-module-a/page-module-a.module").then(
(m) => m.PageModuleAModule
),
data: {
preload: true, // data中的数据可以被守卫获取到
},
},
],
},
],
{
preloadingStrategy: SelectivePreloadingStrategyService,
}
),
],
})
export class PagesRoutingModule {}
- 预加载服务
pages/selective-preloading-strategy.service.ts
import { Injectable } from "@angular/core";
import { PreloadingStrategy, Route } from "@angular/router";
import { Observable, of } from "rxjs";
@Injectable({
providedIn: "root",
})
export class SelectivePreloadingStrategyService implements PreloadingStrategy {
preloadedModules: string[] = [];
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (route.data && route.data["preload"]) {
this.preloadedModules.push(route.path);
return load();
} else {
return of(null);
}
}
}