angular 路由中paramMap和queryParamMap的区别

使用angular的router.navigate()方法进行跳转页面的时候,我们传值的方式有两种:

  • 使用链接参数数组:router.navigate(['/a', {code: '123'}])。构造的链接为/a;code=123.
  • 使用查询参数:router.navigate(['/b'], {queryMap: {code: '123'}})。构造的链接为:/b?code=123.

那么这两种传参方式的关系和区别是啥?我们该如何在正确的场景使用这正确的方法?来研究研究。

paramMap

一个Observable,其中包含一个由当前路由的必要参数和可选参数组成的map对象。用这个map可以获取来自同名参数的单一值或多重值。

首先要区分下必选参数和可选参数。这涉及到路由的定义

必选参数

如何定义一个必选参数:

1
2
3
4
{
path: 'a/:id',
component: APageComponent,
},

上面这样就创建了一个包含必选参数id的路由了,这个路由中的:id等于是在路径中创建了一个空位,这个空位不补全是没法导航的:

1
2
this.router.navigate(['/a']); // 跳转错误,无效路由
this.router.navigate(['/a', 1]); // 正确跳转,跳转url为:xxx/a/1

可选参数

可选参数是在导航期间传送任何复杂信息的理想载体,可选参数不涉及到模式匹配并在表达式上提供了巨大的灵活性。那如何传递一个可选参数:

1
this.router.navigate(['/a', {a: 1, b: 2}]);

上面这种方式跳转的时候,会生成跳转链接:/a;a=1;b=2,对于路由/a来讲,对象里面的ab是可选参数的key。这是通过js来跳转,同样的直接通过页面跳转可以直接写成:

1
<a [routerLink]="['/a', {a: 3, b=2}]">B page</a>

具有同样的效果。

注意:这里数组里的对象只可以是一层,不可以多层。这里转换的方式为第一层对象的key,值会直接转为string。所以,如果是多层对象的话会直接转成[object object],其他比如null也会直接转换为字符串null

上面了解了必选参数和可选参数的声明和使用,页面跳转到对应的目标页面后我们需要去获得这些参数,这时候就需要用到paramMap了。

首先它是一个Observable,那么我们可以通过Observable的方式来拿。

1
2
3
4
5
this.activateRouter.paramMap.pipe(
switchMap(params => of(params.get('a')))
).subscribe((data) => {
console.log('a', data);
});

ActivatedRoute.paramMap属性是一个路由参数的可观察对象,当用户导航到这个组件时,paramMap会发射一个新的值,我们可以在ngOnInit中订阅拿到我们的参数。

这里为什么要加switchMap操作符?可以参考:rxjs操作符-switchMap.

当然,可以使用“快照”(snapshot)的方式获取参数:

1
this.activateRouter.snapshot.paramMap.get('a');

通过快照的方式获取的参数是不需要Observable时的一个简写。这种情况你要保证该url只会用一次(即不会发生从当前url导航到当前url的情况)。因为使用这种方式获取的参数是不会变动的,因为组件的ngOnInit方法只会调用一次,而如果检测到路由相同而参数不同时,是不会重新初始化组件的。

queryParamMap

一个Observable,其中包含一个对所有路由都有效的查询参数组成的map对象。用这个map可以获取来自查询参数的单一或多重值。

如何产生一个查询参数?

使用navigate方法的第二个参数,来看看navigate方法的定义:

1
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean>;

可以知道第二个参数是导航附加功能,里面有个参数queryParams可以为我们的路由加上查询参数:

1
2
3
4
5
 this.router.navigate(['/b', {a: 33], {
queryParams: {
code: 'bbb',
}
});

上面的例子里面,我们的跳转后的链接为:/b;a=33?code=bbba=33这个是可选参数,不用管,code=bbb才是我们需要的查询参数。

和其他参数不同,查询参数是可以在当前url中全部有效(包括子页面、service等其他部分也可以获取到)。

还可以在导航之间保留查询参数:

1
2
3
4
// 比如当前的url是在/b;a=33?code=bbb
this.router.navigate(['/a', 1], {
queryParamsHandling: 'preserve',
});

这样跳转后的链接为:/a/1?code=bbb。可以看到查询参数被保留了。

查询参数有更强大的配置,具体可以参见类型定义:NavigationExtras

查询参数的获取

paramMap和queryParamMap的获取方式都一样,都可以通过Observable的方式和快照的方式进行获取,例如:

1
2
3
4
5
6
7
8
this.activateRouter.queryParamMap.pipe(
switchMap(params => {
return of(params.get('code'));
})
).subscribe((data) => {
console.log('query', data);
});
console.log('b page a query param', this.activateRouter.snapshot.queryParamMap.get('code'));

共同的api

  • has(name) 如果参数名位于参数列表中,就返回 true 。
  • get(name) 如果这个 map 中有参数名对应的参数值(字符串),就返回它,否则返回 null。如果参数值实际上是一个数组,就返回它的第一个元素。
  • getAll(name) 如果这个 map 中有参数名对应的值,就返回一个字符串数组,否则返回空数组。当一个参数名可能对应多个值的时候,请使用 getAll。
  • keys 返回这个 map 中的所有参数名组成的字符串数组。

paramMap和queryParamMap的最大的区别就是,paramMap是针对于当前url,哪怕是当前url的子页面也无法获取到当前页面的参数。而queryParamMap是对所有路由都有效的查询参数,在使用上需要应对不同情况进行选择。一般使用都是针对于当前路由的,需要全局都有效的很少情况,所以一般还是使用paramMap,特殊情况采用queryParamMap。

码字辛苦,打赏个咖啡☕️可好?💘