Angular的路由

Angular的路由

每个模块都可以创建一个路由模块,在根模块的路由模块应该是forRoot,其他路由模块应该是forChild 。RouterModule 可能会被多次导入:每个惰性加载的发布包都会导入一次。 但由于路由器要和全局共享的资源 - location 打交道,所以不能同时激活一个以上的 Router 服务。所以整个应用调用的都是同个Router服务,类似于单例。

这就是需要两种方式来创建本模块的原因:RouterModule.forRootRouterModule.forChild

  • forRoot 创建一个包含所有指令、指定的路由和 Router 服务本身的模块
  • forChild 会创建一个包含所有指令、指定的路由,但不含 Router 服务的模块。
const routes: Routes = [
  {path: "", redirectTo: "heroes", pathMatch: "full"},
  {path: "**", component: NotFoundComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(Routes,{})]
})
class MyNgModule {}

@NgModule({
  imports: [RouterModule.forChild(Routes,{})]
})
class MyNgModule {}

forRootforChild里面可以设置两个参数,第一个参数是必填项是一个Routes数组,Routes数组包含了多个Route信息 。每个Route信息可以设置url以及懒加载等路由设置。第二个参数是可选参数,表示路由器的配置项。

除了用路由指令跳转之外,还可以在逻辑代码中注入Router服务,可以操纵和导航URL。在Router里面我们不仅可以设置路由,还能获取路由的一些状态信息。

在路由跳转的时候经常会带有参数,类似于localhost:4200/heroes/12,或者localhost:4200/heroes?name=张三 这个时候我们就需要注入ActivatedRouteActivatedRoute会包含当前组件相关的路由信息,可以从里面获取路由传过来的参数

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import {Observable} from "rxjs";
import {Hero} from "../hero";
import {HeroService} from "../hero.service";
import {ActivatedRoute, Router} from "@angular/router";


@Component({
  selector: 'app-hero-list',
  templateUrl: './hero-list.component.html',
  styleUrls: ['./hero-list.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeroListComponent implements OnInit {

  constructor(
    private heroService: HeroService,
    private router:Router,
    private activatedRoute: ActivatedRoute
    ) { }

  ngOnInit(): void {
    this.heroes$ = this.heroService.getHeroes();
    this.selected = +this.activatedRoute.snapshot.paramMap.get("id");
  }
    
  onClick() {
      // 点击跳转到 localhost:4200/heroes
      this.router.navigate(['/heroes']); 
  }
    
  getParams() {
      // localhost:4200/heroes/12  输出12
      this.activatedRoute.paramMap.subscribe(hero => console.log(hero.get("id")));
      // localhost:4200/heroes/name=张三  输出张三
      this.activatedRoute.queryParamMap.subscribe(hero => console.log(hero.get("name")))
  }

}

路由参数、查询参数、片段

https://localhost:4200/hero/1?name=德玛#免费英雄

angular路由跳转可以使用router模块提供的routerLink等指令直接用在html代码上进行路由跳转,也可以在ts文件中注入需要的路由服务手动进行跳转,根据不同的业务场景使用其中一种即可。

以下router都为Router注入服务,activatedRoute都为ActivatedRoute注入的服务

1为路由参数
routing文件设置方式:{ path: 'hero/:id', component: HeroDetailComponent }
页面设置:<a [routerLink]="['/hero',id]"></a>
ts: this.router.navgate(['/hero',id]) 

获取路由参数  这里的id是path里面设置的 :id
this.activatedRouter.paramMap.subscribe(param => param.get("id"))
this.activatedRoute.snapshot.paramMap.get("id");

?后面是查询参数, #后面是片段
routing文件不需要变动: { path: 'hero', component: HeroDetailComponent }
// queryParams是查询参数,可以设置多个查询参数 fragment是片段
页面设置:<a [routerLink]="['/hero']" [queryParams]="{name: '德玛',parice: '6300'}" [fragment]="'免费英雄'"></a>
ts:this.router.navigate(['/heroes'], {
      queryParams: {name: "德玛", price: "6300"}, //查询参数
      fragment: "免费英雄"  // 片段
    })
获取查询参数和片段:
this.activatedRouter.fragment.subscribe(value => console.log(value));
this.activatedRouter.queryParamMap.subscribe(value=>console.log(value.get("name"),value.get("price")));

路由守卫

守卫返回一个值,以控制路由器的行为:

  • 如果它返回 true,导航过程会继续
  • 如果它返回 false,导航过程就会终止,且用户留在原地。
  • 如果它返回 UrlTree,则取消当前的导航,并且开始导航到返回的这个 UrlTree.
  • 一个路由可以有多个守卫,要想成功跳转所有守卫都为返回true
  • 守卫返回的是一个Observable对象,这个对象必须是可结束的,否则,导航就不会继续

路由器可以支持多种守卫接口:

  • CanActivate来处理导航某路由的情况。

    很多页面是需要用户登录以后才能操作的,使用此路由拦截未登录的用户给他跳转到登录页面

  • CanActivateChild来处理导航某子路由的情况。

  • CanDeactivate来处理从当前路由离开的情况.

    当用户输入信息为保存的时候我们应该在他跳转下一个路由的时候提醒他

  • Resolve在路由激活之前获取路由数据。

    如果你在使用真实 api,很有可能数据返回有延迟,导致无法即时显示。 在这种情况下,直到数据到达前,显示一个空的组件不是最好的用户体验。最好使用解析器预先从服务器上获取完数据,这样在路由激活的那一刻数据就准备好了。

  • CanLoad来处理异步导航到某特性模块的情况。

    我们已经使用CanActivate守卫限制了未登录的用户,但是用户点击需要登录的页面的时候路由还是会加载这个页面的模块,最好的情况是用户登录以后点击我们才会加载这个模块,canLoad守卫就能在用户登录以后点击才会加载一次当前模块,这也属于路由的异步加载方式,所以必须使用懒加载的路由才能设置canLoad守卫。

所有的守卫接口都是一个带有@Injectable装饰器的服务,实现了各自接口的方法,一般一个守卫服务只会实现一个守卫接口,下面实现多个为演示作用。

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  UrlTree,
  Router,
  CanActivateChild, CanLoad, Route, UrlSegment
} from '@angular/router';
import { Observable } from 'rxjs';
import {AuthService} from "./auth.service";

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate,CanActivateChild,CanLoad {

  constructor(private authService: AuthService,private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
    console.log('AuthGuard#canActivate called');
    let url = state.url;
    console.log("url", url);
    return this.checkLogin(url);
  }

  private checkLogin(url: string): true | UrlTree {
    if (this.authService.isLoggedIn) {
      return true;
    }
    this.authService.redirectUrl = url;

    return this.router.createUrlTree(["/login"], {
      queryParams: {sessionId: 123456},
      fragment: "anchor"
    });
  }

  canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
    return this.canActivate(childRoute, state);
  }

  canLoad(route: Route, segments: UrlSegment[]): boolean | UrlTree {
    let url = `${route.path}`;
    return this.checkLogin(url);
  }
}

路由懒加载

在很多情况下,有些模块不需要立即使用或者使用频率比较低我们就可以使用按需加载或者预加载,提高页面效率

使用loadChildren实现按需加载,当第一次访问这个路由的时候才会加载路由里面的模块
{
    path: "admin",
    loadChildren: () => import('./pages/router-study/admin/admin.module').then(m => m.AdminModule),
    // canLoad: [AuthGuard], // canLoad守卫守护的路由是不会被预加载的
},

    
angular预加载默认是全部都不预加载,我们也可以设置preloadingStrategy属性设置为PreloadAllModules(此模块为angular自身提供的)全部都预加载。但是一次全部预加载并不灵活,我们可是设置自定义预加载策略。SelectivePreloadingStrategyService服务就是自定义的预加载策略
@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
    onSameUrlNavigation: "ignore",
    // preloadingStrategy: PreloadAllModules  // 设置懒加载的路由预加载,angular默认懒加载模块全都不预加载,设置PreloadAllModules为全部预加载
    preloadingStrategy: SelectivePreloadingStrategyService,
    relativeLinkResolution: 'legacy'
})],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

/*
SelectivePreloadingStrategyService预加载服务实现了PreloadingStrategy接口,全部预加载PreloadAllModules也是实现的此接口。在服务中我们设定在route里面data属性带有preload为true的则实现预加载
*/
@Injectable({
  providedIn: 'root',
})
export class SelectivePreloadingStrategyService implements PreloadingStrategy {
  preloadedModules: string[] = [];

  preload(route: Route, load: () => Observable<any>): Observable<any> {
    if (route.data && route.data['preload']) {
      // add the route path to the preloaded module array
      this.preloadedModules.push(route.path);
      return load();
    } else {
      return of(null);
    }
  }
}
    
{
    path: "crisis-content",
    loadChildren: () => import('./pages/router-study/crisis-content/crisis-content.module').then(m => m.CrisisContentModule),
    data: {preload: true} // 带有preload为true则预加载
},

路由事件

每次进行路由跳转都会触发一系列的路由事件

在RouterMoudel.forRoot(router,{enableTracing: true})设置enableTracing为true则可以在控制台打印路由事件

我们也可以在ts中注入Router来获取路由事件

在event中包含了路由的所有事件,路由监听不是只会监听当前ts文件模块的路由变化,而是整个应用的路由变化都会监听

ngOnInit(): void {
    this.router.events.pipe(
      filter(event => event instanceof NavigationStart)
    ).subscribe((event: NavigationStart) => {
      console.log(event);
      console.log(this.router.parseUrl(event.url));
    });
}
路由事件说明
NavigationStart导航开始时触发的事件
RouteConfigLoadStartRouter 惰性加载路由配置之前触发的事件
RouteConfigLoadEnd在某个路由已经惰性加载完毕时触发的事件
RoutesRecognized当路由器解析了 URL,而且路由已经识别完毕时触发的事件
GuardsCheckStart当路由器开始进入路由守卫阶段时触发的事件
ChildActivationStart当路由器开始激活某路由的子路由时触发的事件
ActivationStart当路由器开始激活某个路由时触发的事件
GuardsCheckEnd当路由器成功结束了路由守卫阶段时触发的事件
ResolveStart当路由器开始路由解析阶段时触发的事件
ResolveEnd当路由器的路由解析阶段成功完成时触发的事件
ChildActivationEnd当路由器成功激活某路由的子路由时触发的事件
ActivationEnd当路由器成功激活了某个路由时触发的事件
NavigationEnd当导航成功结束时触发的事件
NavigationCancel当导航被取消时触发的事件。 这可能在导航期间某个路由守卫返回了 false 或返回了 UrlTree 以进行重定向时发生。
NavigationError当导航由于非预期的错误而失败时触发的事件
Scroll用来表示滚动的事件
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值