tap() 与 subscribe() 设置 class 属性

tap() vs subscribe() to set a class property

我是 rxjs 的新手,只是想知道是否可以通过管道传输流并点击它来设置 class 属性,或者我应该在订阅中进行设置。对我来说这两种方法都有效,只是想知道是否可以按照我认为合适的方式去做,或者有一些我不知道的事情。

演示两种方式的 Typescript 代码:

export class ViewComponent implements OnInit {

  applicant = {};

  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() {
    this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')])),
      tap(applicant => this.applicant = applicant)
    ).subscribe();
  }
}

export class ViewComponent implements OnInit {

  applicant = {};

  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() {
    this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')]))
    ).subscribe(applicant => this.applicant = applicant);
  }
}

好问题。在 tap 运算符的 source code 中,这条评论几乎概括了它:

This operator is useful for debugging your Observables for the correct values or performing other side effects.
Note: this is different to a subscribe on the Observable. If the Observable returned by do is not subscribed, the side effects specified by the Observer will never happen. do therefore simply spies on existing execution, it does not trigger an execution to happen like subscribe does.

您可以在 tap 中 运行 的任何副作用也可以放在 subscribe 块中。 subscribe 表示您打算积极使用源值,因为它说的是 "when this observable emits, I want to save it's value in the applicants variable"。 tap 运算符主要用于调试,但它 可以 用于 运行 副作用。

一般来说,为了 运行ning 副作用,使用 subscribe 块,使用 tap 进行调试,但请注意,如果需要,tap 可以做更多

tap 当您将可观察对象与其订阅者分开时很有用。如果你有一个 class 公开了一个可观察对象,你可以使用 tap 来实现这个 class 需要在有人 listening 时执行的副作用] 到可观察的。另一方面,当您从另一个 class 订阅它时,您可以使用 subscribe.

从订阅者的角度实现副作用

Class 与可观察的:

public dummyObservable: Observable<number> = from([1, 2, 3, 4, 5]).pipe(
  // Side effects, executed every time I emit a value
  // I don't know which side effects implements who subscribes to me
  tap( n => console.log("I'm emitting this value:", n) )
);

Class 订阅:

ngOnInit(): void {
  this.dummyService.dummyObservable.subscribe(
    // Side effects, executed every time I receive a value
    // I don't know which side effects implements the observable
    data => console.log("I'm receiving this value: ", data)
  );
}

Michael Hladky 建议您将所有副作用放在 tap 运算符中,他解释了原因 here

我认为这样做通常是个好主意,因为正如 Michael 所说,然后您可以将许多可观察对象合并在一起并为所有这些对象创建一个订阅。

我不知道这是否会提高性能,但当您想退订到只有一个订阅时,它肯定会更容易。 取消订阅是您始终应该做的事情,以避免可能的内存泄漏或其他奇怪的行为。

这种方法的另一个好处是,您可以轻松地暂停、恢复或完成一组 observable,方法是通过 filter 或 takeWhile 等运算符将其管道化,或者通过另一个 observable 切换映射,如下所示:

const allMergedObservables$ = merge(obs1, obs2, obs3);
const play$ = new Subject();

play$.asObservable().pipe(
    switchMap(bool => bool ? allMergedObservables$ : EMPTY)
).subscribe();

// Putting 'true' into the play stream activates allMergedObservables$. 
play$.next(true);

// Something happens that makes you want to pause the application,
// for instance the user opens the print dialog box,
// so you issue 'false' in the play stream which in turn stops the
// inner subscription of allMergedObservables$:
play$.next(false);

但是,这取决于您和您喜欢的任何编程风格。

AsyncPipeNgrxPushPipe

对于那些想使用 async pipe or ngrxPush 管道的人,您别无选择,只能使用 tap 运算符

在上面的例子中我们可以有这样的东西

export class ViewComponent implements OnInit {
  
  applicant = {};
  applicant$ = this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => state.applicants.entities[params.get('id')])),
      tap(applicant => this.applicant = applicant)
    )
  constructor(public route: ActivatedRoute, private store: Store<any>) {}

  ngOnInit() { }
}

并在 html

使用AsyncPipe

<ng-container *ngIf='applicant$ | async'>

   ...Some Html code here

</ng-container>

使用 NgrxPushPipe(请记住,这仅在您 import { ReactiveComponent } from '@ngrx/component' 时有效)

<ng-container *ngIf='applicant$ | ngrxPush'>
 
...Some Html code here

</ng-container>

增加

以上两个管道有助于提高代码的可维护性和reduces/removes由于未订阅的可观察对象而导致内存泄漏的风险

以上代码可以简化为

TS 文件

export class ViewComponent {
 
  applicant$ = this.route.paramMap.pipe(
      switchMap(params => this.store.select(state => 
        state.applicants.entities[params.get('id')])
      )
    )
  constructor(public route: ActivatedRoute, private store: Store<any>) {}

}

HTML 文件

使用AsyncPipe

<ng-container *ngIf='applicant$ | async as applicant'>

   ...Some Html code here

</ng-container>

使用NgrxPushPipe

<ng-container *ngIf='applicant$ | ngrxPush as applicant'>

   ...Some Html code here

</ng-container>

使用NgrxPushPipe

<ng-container *ngIf='applicant$ | ngrxPush as applicant'>

   ...Some Html code here

</ng-container>

使用 ngrxLet 结构指令(也来自“@ngrx/component”)

<ng-container *ngrxLet='applicant$; let applicant'>

   ...Some Html code here

</ng-container>