Rxjs 中的 Subject

114 阅读1分钟

Subject是一种特殊的Observable,用来创建多播Observable

演示代码使用vue3

我最常用的就是Subject,它是基础的Subject,没有特殊的初始值或缓存功能。可以通过调用next方法手动发出新的值,比如可以用在以下场景:

在table中实现多选功能

image.png 表头第一列和行第一列单元格是不同的组件,当表头组件checkbox变化时,需要勾上/取消行单元格组件中的checkbox,这个时候就可以使用Subject实现表头变化时实时通知表格

先定义一个Subject,结合pinia定义一个全局的Subject

export const useCheckboxAllSelectedStore = (tableId: string) => {
  return defineStore('AllSelectedStore_' + tableId, () => {
    const selectAllStatusStore$ = ref(new BehaviorSubject<boolean>(false));

    const setAllStatusStore = (status: boolean): void => {
      selectAllStatusStore$.value.next(status);
    };

    const getAllSelectedStatusStore = (): Observable<boolean> => {
      return selectAllStatusStore$.value.asObservable();
    };

    return {
      setAllStatusStore,
      getAllSelectedStatusStore,
    };
  })();
};

在表头组件中next值出去:

  const getCheckedStatus = (status: boolean): void => {
     const _useCheckboxAllSelectedStore = useCheckboxAllSelectedStore(tableId);
     // 设置当前全选状态
     _useCheckboxAllSelectedStore.setAllStatusStore(status);
  }

在行单元格组件中订阅:

  const getCheckedStatus = (status: boolean): void => {
     const _useCheckboxAllSelectedStore = useCheckboxAllSelectedStore(tableId);
   // 将checkbox勾选/取消 
    _useCheckboxAllSelectedStore.getAllStatusStore(status).subscribe((allSelectedStatus: boolean) => {
    checkedStatus.value = allSelectedStatus;
  });
};

勾选行计算全选/半选状态也是同样的思路

Subject有个特点,在rxjs专栏的另外一篇文章说过,如果订阅时机在next之后,那订阅不到已经发出的值了,只能订阅到执行订阅动作后next出的值。

基于以上,如果我有以下需求

一、无论何时订阅,我想要拿到所有next出的值

ReplaySubject 就能满足,它会存储Observable发出的旧值,并且当新的观察者订阅时,它可以接收到之前发出的值序列

二、无论何时订阅,不需要所有的值,需要拿到订阅后最后一次next值及后面next值,

BehaviorSubject 就能满足,它允许你设置一个初始值,并且在订阅该BehaviorSubject时会立即接收到最近的值或初始值。当新的观察者订阅时,它们会立即收到BehaviorSubject的当前值,然后继续收到任何将来发出的新值

三、只有一个值,比如发射初始化完成状态

在开发中我遇到了一个场景,就是初始化的时候需要执行一段逻辑,但是呢由于某个包没有初始化完成,导致报错,那我得知道什么时候初始化完成了,然后再去执行那段逻辑,这个时候就需要把初始化完成状态发射出去,我在组件订阅,订阅到了再去执行逻辑

AsyncSubject 就能满足,它只会在Observable完成时,发出最新的值给观察者。 注意完成二字,下面是个例子:

定义一个AsyncSubject

export const useAgGridReadyStore = (tableId: string) => {
  return defineStore('AgGridReadyStore_' + tableId, () => {
    const agGridReadyStore$ = ref(new AsyncSubject<boolean>());

    const seAgGridReadyStore = (res: boolean): void => {
      agGridReadyStore$.value.next(res);
      agGridReadyStore$.value.complete();
    };

    const getAgGridReadyStore = (): Observable<boolean> => {
      return agGridReadyStore$.value.asObservable();
    };

    return {
      seAgGridReadyStore,
      getAgGridReadyStore,
    };
  })();
};

注意第7行,next后直接执行complete(), 就会把最新值发送出去,上述例子只有一个值,虽然用Subject也能实现同样的功能,但是使用AsyncSubject性能会更好些。