5

引言

Web应用,因为组件化大行其道,诞生了一个问题。

image.png

如上图所示,顶部导航栏组件需要请求当前登录用户的姓名,左侧菜单栏组件需要请求当前登录用户的菜单权限,根据后台的接口设计,两者都需要在应用初始化时获取当前登录用户。

导航栏发起HTTP请求,获取当前登录用户信息。

菜单栏发起HTTP请求,获取当前登录用户信息。

两者请求到的数据是相同的,完全可以共用一个请求来完成这个任务,请求两次无疑耗费网络资源。

但是就这么一个简单的任务,却迭代了好几个实现版本才完美解决。

实现

问题示范

如下所示,多次请求同一接口数据,实际业务场景肯定是跨组件的,这里只是举一个最简单的例子:

export class AppComponent implements OnInit {

  constructor(private httpClient: HttpClient) {
  }

  ngOnInit(): void {
    this.getTeachers();
  }

  getTeachers(): void {
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 1', teachers);
    });
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 2', teachers);
    });
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 3', teachers);
    });
  }

  request(): Observable<Array<Teacher>> {
    return this.httpClient.get<Array<Teacher>>('/assets/mock/teacher.json');
  }
}

三个调用方都拿到了数据:

image.png

同一个接口,发起了三次HTTP请求:

image.png

初代版本

流程如下:

image.png

自定义一个观察者对象,所有要获取当前登录用户的组件要去currentLoginUser$观察者上进行订阅。

系统初始化时,请求接口数据,将结果next进观察者对象中。

  • 确实解决了多次请求的问题。
  • 因为初始化时请求,所以当数据变更时,再订阅到的数据还是旧数据。
  • 思维困难,需要实现者深入理解观察者模式。

迭代

最新在StackOverflow上发现可以采用RxJS share方式完美地解决该问题,如下所示:

定义一个观察者对象teachers$,但是这个对象不需要我们自己维护,我们只需要调用httpClient的方法,将它返回的可观察对象使用share操作符过滤即可。

之后改造request方法,返回teachers$可观察对象。

export class AppComponent implements OnInit {

  teachers$: Observable<Array<Teacher>>;

  constructor(private httpClient: HttpClient) {
    this.teachers$ = this.httpClient.get<Array<Teacher>>('/assets/mock/teacher.json').pipe(share());
  }

  ngOnInit(): void {
    this.getTeachers();
  }

  getTeachers(): void {
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 1', teachers);
    });
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 2', teachers);
    });
    this.request().subscribe((teachers: Array<Teacher>) => {
      console.log('request - 3', teachers);
    });
  }

  request(): Observable<Array<Teacher>> {
    return this.teachers$;
  }
}

三个调用方都拿到了数据:

image.png

同一个接口,只发起了一次HTTP请求,节省了网络资源:

image.png

  • 同样实现HTTP复用功能。
  • 使用方法简单,都是模板代码,即便初学者也可按照范例进行编写。

share

RxJS相当复杂,这里的share也只是它的冰山一角。

share运算符不太好理解,RxJS相关的概念太多,这里就不一一展开了,请参考文章:译 RxJS: 理解 publish 和 share 操作符

当然,如果你没有足够地时间去学习探究share运算符,就大胆地使用上述示例代码吧,这,就是最佳实践。

总结

RxJS基于观察者,但不止于观察者,RxJS存在着众多的操作符,且都不是那么好理解,太伟大了。


张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。