路由知识总结
可以把SPA(single page application)理解为是一个视图状态的集合。Angular架构下的各个视图会因为操作的不同显示的也会各有千秋,这些功劳全都得归功于路由。
基础知识
路由相关的对象总结:
1.Routes:路由配置,表示在哪个URL中会显示哪个组件,还有就是在哪个RouterOutlet(像是一个插排一样)中显示组件。
/**
* Tips: path 不能使用斜杠进行开头,因为可以让Angular自动使用绝对路径和相对路径。
* 配置好路径以及路径对应的需要显示的component。
*/
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'products', component: ProductsComponent}
];
2.RouterOutlet:在HTML标记路由内的占位符指令。
<router-outlet></router-outlet>
3.Router:在运行时指定路由的行为,通过navigate()以及navigateByURL()指定路由到哪个路由中去。
//html模版上写入一个点击事件,通过事件,触发clickProductButton事件。通过router实现路由。
constructor(
private router: Router
) {}
public clickProductButton() {
this.router.navigate(['/products']);
}
4.RouterLink:在HTML中声明路由导航用的指令。与Router相类似,只不过Router是在controller中使用的,而RouterLink在HTML中使用的。
<!--必须加入斜杠,因为这样才能区分是跟路由,还是子路由-->
<!--为什么 routerLink的值是一个数组呢,因为可以通过路由传入一些参数-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/products']">商品详情</a>
5.ActivatedRoute:当前激活路由的相关信息,可以被这个类记录,并且被我们使用。
如何在路由中传递数据
1.在查询参数中传递数据
多添加一个[queryParams]的属性绑定形如:
<a [routerLink]="['/products']" [queryParams]= "{id:1}">商品详情</a>
获取:通过ActivatedRoute.queryParams[参数的key]
2.在路由路径中传递数据
- 修改Routes中的path属性,形如:path:'product/:type'
- routerLink中多添加一个参数,形如:[routerLink]="['/products','book']" ,这里的book就是给我们刚刚定义type的值。
获取:通过 ActivatedRoute.params[参数的key]
3.在路由配置中传递数据
通过在Routes中定义data参数 形如:
{path: '', component: HomeComponent, data: [{key: value}]}
然后通过ActivatedRoute.data[0] [key] 来获取
Tips:参数快照与参数订阅
首先上代码:
//参数订阅
this.activatedRoute.params.subscribe((params: Params) => {
this.productType = params['type'];
});
//参数快照
this.productType = this.activatedRoute.snapshot.params['type'];
他俩的区别就在于我们现在有两个不同的按钮,跳转到的URL分别为 [routerLink]="['/products','book']",和[routerLink]="['/products','watch']",可以看出它们只有type的参数类型不同。
如果使用了快照,点击了第一个按钮,在点击第二个,那么获取到的参数不会发生变化,这个时候我们就应该使用参数订阅(观察者模式的思想,感兴趣的可以查询RXJS,进行详细了解)。
重定向路由
在Routes中添加 对应参数:
{path: '', redirectTo: '/home', pathMatch: 'full'}
子路由
在正常的情况下,组件与组件之间一定是会有嵌套关系的,这种嵌套关系就会导致我们的路由插座()同样也是嵌套的。子路由就是为了解决路由插座父子嵌套关系的
使用子路由的步骤:
1.修改在Routes中,product的路由信息,主要就是添加了一个children属性:
{path: 'products/:type', component: ProductsComponent, children: [
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerComponent}
]}
2.在需要子路由的html中,插上 作为插座
3.然后在需要跳转的地方编写如下代码
<a [routerLink] = "['./']">跳转到商品详情</a>
<a [routerLink] = "['./seller', 99]">跳转到售货员信息</a>
辅助路由
刚刚的子路由如果说是父子关系的话,那么辅助路由就是"兄弟关系了"。
这种场景出现在我们在一个界面中,两个component分别被不同的路由机制管理着,如果只使用原来的插槽,没有办法指定用的到底是哪一种路由策略,所以辅助路由就这么诞生了。
使用辅助路由的步骤:
1.> 通过name 指定具体的路由插座名称
2.> 指定当前这个aux路由可以展示哪些component。
{path: /xxx, component: XxxComponent, outlet: aux}
{path: /yyy, component: YyyComponent, outlet: aux}
3.> 在进行导航的地方指定我们需要的那个路由
路由守卫
页面从一种页面状态跳转到另一种页面状态,有的时候需要一些条件,检查这些条件就是路由守卫的职责。
一共可以分为三种:
1.CanActivate: 处理导航到某路由的情况
大概的使用步骤:
首先我们先要写一个守卫的类:
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"; /** * 这个路由守卫用于实现进入某以页面需要满足某些需求的情况。 */ export class LoginGuard implements CanActivate { private flag = true; canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { //这里给了一个随机数,如果数字大于0.5则可以进行登陆,否则会被拦截 this.flag = Math.random() > 0.5; if ( this.flag ) { console.log('可以登陆'); } console.log(this.flag); return this.flag; } }
然后将守卫的类添加到Routes中需要守卫的规则中:
{path: 'products/:type', component: ProductsComponent, canActivate: [LoginGuard], children: [ {path: '', component: ProductDescComponent}, {path: 'seller/:id', component: SellerComponent} ]}
最后在app.module.ts中添加自己需要依赖注入的守卫类即可:
providers: [LoginGuard]
2.CanDeactive: 处理从当前路由离开的情况
大概的使用步骤:
首先我们先要写一个守卫的类:
import { CanDeactivate, ActivatedRouteSnapshot } from "@angular/router"; import { ProductsComponent } from "../products/products.component"; export class NotSaveGuard implements CanDeactivate<ProductsComponent> { private flag = true; canDeactivate(component: ProductsComponent, _currentRoute: ActivatedRouteSnapshot) { //这里暂时给出一个提示框 return window.confirm("还没有保存确定离开吗?"); } }
然后将守卫的类添加到Routes中需要守卫的规则中:
{path: 'products/:type', component: ProductsComponent, canDeActivate: [NotSaveGuard], children: [ {path: '', component: ProductDescComponent}, {path: 'seller/:id', component: SellerComponent} ]}
最后在app.module.ts中添加自己需要依赖注入的守卫类即可:
providers: [NotSaveGuard]
3.Resolve:在路由激活之前获取数据
在进入路由之前检测数据是不是已经存在,以为网络请求具有延迟,如果出现了,已经路由到下个界面,但是信息还没有存在的情况,我们就会让界面路由到错误界面或者别的什么界面。
大概的使用步骤:
1.首先我们定义一个Resolve守卫的类:
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router"; import { ProductsComponent, Product } from "../products/products.component"; import { Injectable } from "@angular/core"; @Injectable() export class ProductGuard implements Resolve<Product> { constructor(private router: Router) { } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (route.params['type'] === 'book') { return new Product(1, 'iphone X'); } else { this.router.navigate(['/home']); return undefined; } } }
2.然后将resolve属性添加到守卫的规则中
{path: 'products/:type', component: ProductsComponent, resolve: {product: ProductGuard}, children: [ {path: '', component: ProductDescComponent}, {path: 'seller/:id', component: SellerComponent} ]}
3.依赖注入 ProductGuard
providers: [ProductGuard]
4.resolve 其实相当于对返回值的一种增强,接受返回值的地方我们应该这么写
this.activatedRoute.data.subscribe((data: {product: Product}) => { //注意:这里之所以可以使用data.product,是因为我们在Routes路由中配置的 resolve: {product: ProductGuard}所致。这里的product就是返回值的名字,如果变化了,两个名字都要一起变化。 this.productId = data.product.id; this.productName = data.product.name; });
最后附加上本文提及到的代码,我已经放在github上,可供参考