angular 指定components的路径_理解angular路由的导航周期

路由对于单页应用来说是至关重要的。在单页应用中,页面只在应用启动的时候加载一次,当用户需要在应用的不同内容之间切换时必须通过路由来实现。

angular路由的配置还是非常简单的,但是你有没有想过当点击一个链接,触发路由导航时究竟发生了哪些事情?这篇文章中我们将讨论这个问题,也就是路由导航的周期。

在这里先提出三个问题:

  1. 对于特定的url,应该被导航到哪一个组件?

  2. 是否能通过路由导航到某个组件上?

  3. 是否需要在导航到这些组件之前获取一些数据?

带着这些问题我们来了解导航的细节:

  • 路由从开始到结束的整个处理过程。

  • 路由是如何被构建的,ActivatedRouteSnapshot对象的使用方法。

  • 使用指令渲染路由的内容。

下面这张图呈现了整个路由导航的过程:

900255d2803c55591a9875eaa00c6a7a.png

导航

angular创建的是一个单页应用,也就是说当页面的url变化时并不会重新加载一个新的页面,而是在浏览器中基于url上的位置信息展示相应的内容,对于用户来说就好像看到了一个新的页面。

url的变化会触发导航行为的发生,也就是触发路由行为,这与使用锚点,通过href获取新的页面不同,后者会导致整个页面重新加载。因此angular中给我们提供了[routeLink]指令,当点击动作发生时,这个指令会通知路由更新url,同时把相应的内容渲染到指定的位置上,而不是重新加载整个页面。

href='localhost:4200/users'>Users  

[routerLink]="['/users']">Users  

导航发生的时候,在页面呈现对应的组件之前会发生一系列其它的动作,导航成功之后新的组件被渲染到指定的地方,同时ActivatedRoute对象上会生成一个数形结构的数据以保存导航记录,目前我们只需要知道angular的路由会使用这个对象上的数据,当然开发人员也可以使用这个对象来查询路由的相关参数等。

示例

完整的示例可以在这里找到。

下面是这个示例的路由配置:

const ROUTES = [

 { path: 'users',

   component: UsersComponent,

   canActivate: [CanActivateGuard],

   resolve: {

     users: UserResolver

   }

 }

];

应用导航发生时会检查查询参数以确定用户是否已经登录(登录时:login=1),然后通过mock服务取回一些用户信息展示到页面上。

我们不需要关注这个应用的细节,只是想通过它来解释一下导航发生的整个过程。

导航周期和路由事件

angular里提供了Router服务,可以通过订阅它查看到导航时发生时的所有事件。

constructor(private router: Router) {

 this.router.events.subscribe( (event: RouterEvent) => console.log(event))

}

在开发的环境中也可以通过路由配置来查看导航发生时的所有事件。

RouterModule.forRoot(ROUTES, {

 enableTracing: true

})

在控制台上可以看到导航到 /users 时路由上发生的所有事件。

100afd145652050076af71fce5276736.png

这些事件对于学习或调试路由来说很有用,比如可以通过订阅这些事件来很方便的添加一些提示信息。

ngOnInit() {

 this.router.events.subscribe(evt => {

   if (evt instanceof NavigationStart) {

     this.message = 'Loading...';

     this.displayMessage = true;

   }

   if (evt instanceof NavigationEnd) this.displayMessage = false;

 });

}

当导航开始时,会显示'loading...',导航结束时消失。

导航开始

对应的事件:NavigationStart

在示例中,用户通过这个链接触发导航:

[routerLink]="['/users']" [queryParams]="{'login': '1'}">Authorized Navigation

在导航发生时,路由上同时会传递一个查询参数:login=1

用户一旦点击了这个链接就会触发导航,当然触发导航的方法不只这一种,还可以通过Router服务的方法,比如:navigate和navigateByUrl方法。

匹配Url和重定向

对应的事件: RouterRecognized

0c7ea94df1bdee55a137bb91442efe2c.png

路由使用深度优先的原则在配置中查找url对应的路径,配置就是示例中定义好的ROUTES,如果配置上有重定向设置,此时会触发重定向。

显然在这个配置中并没有重定向,因此url:/users 将会匹配到写好的这个配置:

{ path: 'users', component: UsersComponent, ... }

如果匹配到的路径需要一个懒加载模块,此时将会从后台加载这个模块。

此时路由会发出RoutesRecognized事件以表明已经匹配到了一个url,接下来将会导航到某一个组件(UsersComponent)。文章开头提到的第一个问题在这里似乎可以被解答了,但是还得稍等一下,因为路由还需要确定一下是否允许导航到这个组件。

路由守卫

对应的事件:GuardsCheckStart, GuardsCheckEnd

e0ce5c08a19d6184252ab5e0f0ddc59e.png

路由守卫本质上就是一些返回布尔值的函数,通过返回的布尔值决定导航动作是否允许发生。作为开发者,可以通过设置路由守卫来控制导航是否可以发生。

在我们的示例中使用了canActivate守卫来检查用户是否已经登录。路由守卫需要在配置中指定。

{ path: 'users', ..., canActivate: [CanActivateGuard] }

下面是守卫函数:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {

   return this.auth.isAuthorized(route.queryParams.login);

}

在这个函数中给 auth 服务的方法传入了路由上的查询参数,如果这个函数返回true,导航动作将被允许,否则被禁止。如果禁止导航,路由上会发出 NavigationCancel 事件,整个导航过程将被终止。

对于其它的路由守卫,比如:canLoad,canActivateChild,canDeactivate执行过程是一样的,只是功能不同。

守卫和服务一样,也是被注册在providers上的,可以通过依赖注入的方式使用。一旦url变化,路由被触发,这些设置在路由配置中的守卫就会运行。

现在就可以回答文章开头提到的第二个问题了,如果通过了路由守卫,接下来就可以获取数据。

需要注意的是,canActivate 守卫的运行肯定是先于获取数据的 resolve 守卫的,这很好理解,如果导航不能发生那么根本没有必要去获取数据。

路由解析器

对应的事件:ResolveStart,ResolveEnd

e0ce5c08a19d6184252ab5e0f0ddc59e.png

解析器本质也是函数,只是功能不同。它可以在导航的过程中预先获取数据,也就是说数据获取的过程发生在页面渲染之前。

在路由配置中也是通过指定一个字段添加resolve守卫:

{ path: 'users', ..., resolve: { users: UserResolver } }

userResolve的实现

export class UserResolver implements Resolve<Observable> {

 constructor(private userService: MockUserDataService) {}

 resolve(): Observable {

   return this.userService.getUsers();

 }

}

在通过了路由守卫的校验之后,路由将会使用这个resolve方法来获取数据,并将获取到的数据保存在ActivatedRoute服务上data对象下的users字段中(因为在配置中指定了守卫对应的key是users)。通过订阅data对象就可以得到已经获取到的数据。

activatedRouteService.data.subscribe(data => data.users);

在这个示例中,我们在UsersComponent上使用了ActivatedRoute服务来获取resolve拿到的数据。

export class UsersComponent implements OnInit {

 public users = [];

 constructor(private route: ActivatedRoute) {}

 ngOnInit() {

   this.route.data.subscribe(data => this.users = data.users);

 }

}

Resolvers允许在导航过程中预先获取组件中所需要的数据 这可以避免给用户呈现一个不完整的组件,我们知道,组件的OnInit过程发生之后用户就可以看到对应的模板,如果此时再去获取数据的话,用户看到的模板可能是不完整的,因为数据还没有获取回来。

然而,有时先让用户看到一个不完整的模板可能更加合适 先加载数据还是先呈现页面完全取决于开发者。先让用户看到一个不完整的页面,同时配合一些加载动画等可能更加有利于提高用户体验。

一旦数据获取完成,接下来就是开始将对应的组件渲染到对应的router outlet指定的地方。

激活路由

对应的事件:ActivationStart, ActivationEnd, ChildActivationStart, ChildActivationEnd

66fe422fbb5059b4389578f5a154f4db.png

路由通过从ActivatedRouteSnapshots树上查询信息,把对应的组件渲染到恰当的位置。路由配置中的component属性表明了需要实例化一个UsersComponent组件,同时可以看到users数据已经被取回来,放在data对象的users字段下。

7017992354f37f63cf19d37ae8b53304.png

这个过程是通过路由上的 activatedWith 方法来实现的。

56fded595e748450170c08a8c3afc1aa.png

这里不过分纠结细节,主要看几个关键点:

  • 第9行,使用ComponentFactoryResolver创建了一个组件,组件的信息是在第7行通过ActivatedRouteSnapshot获取到的。

  • 第12行,location就是router-outlet对应的ViewContainerRef,表明了在哪个位置激活路由。

路由对应的组件被渲染出来后,如果模板中还有router-outlet,也就是说有嵌套的子路由,将接着执行这个过程,把子路由上的组件也渲染出来。

更新url

最后一步,更新页面的url,显示对应的路径 /users

da2158c16eeb557101b572cad97ec1fd.png

updateTargetUrlAndHref(): void {

 this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));

}

路由继续监听url的变化,如果url改变了,以上过程将再次执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Angular 中,导航栏可以作为一个抽象由来实现。抽象由是指在由配置中定义的一种特殊由,它不会对应一个具体的组件或页面,而是用于组织和管理其他由。 以下是如何将导航栏作为抽象由的步骤: 1. 创建导航栏组件:首先,创建一个导航栏组件,用于显示导航菜单和处理导航逻辑。这个组件不需要对应一个具体的路径,因为它将作为抽象由使用。 2. 定义抽象由:在由配置文件中,定义一个抽象由,例如`{ path: '', component: NavbarComponent, outlet: 'navbar' }`。这里使用一个特殊的`outlet`属性来指定抽象由的出口名称为`navbar`,以便在其他由中插入导航栏。 3. 插入导航栏:在其他具体由的模板中,使用`<router-outlet>`元素来指定插入抽象由的位置。例如,可以在主模板中添加`<router-outlet name="navbar"></router-outlet>`,这样导航栏就会被插入到主模板的指定位置。 通过以上步骤,导航栏就作为一个抽象由被集成到应用中了。它可以在不同的页面中显示,并且可以处理导航逻辑。 使用抽象由的好处是,可以将导航栏与其他具体由组件解耦,使其独立于具体的页面。这样可以更容易地扩展和维护导航栏功能,同时保持代码的整洁和可读性。 希望以上解答对你有帮助,如果还有其他问题,请随时提问!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值