RxJS 是一个用于处理异步事件的库,Subject 是 RxJS 中非常常用的一个概念,最常被用于事件的发布与订阅中。假设你已经了解了 RxJS 基础的 Observables 和 Operators,那么接下来我们来看一下什么是 RxJS 的 Subject,它有什么作用以及应该如何使用。
Subject 的定义
Subject 是 RxJS 中最基础的概念之一,它是一个特殊类型的 Observable,既可以订阅也可以发送事件,同时也可以作为 Observer 来接收其它 Observable 或 Subject 发射出来的事件。
具体来说,我们可以通过 new Subject() 来创建一个 Subject 实例,它具备以下能力:
- 发送事件
Subject 实例可以通过 .next(value) 方法来发送一个值。
const subject = new Subject(); subject.next('hello'); subject.next('world');
- 订阅事件
Subject 实例可以通过 .subscribe(callback) 或 .subscribe(observer) 来订阅事件。这与一般的 Observable 并没有什么不同。
const subject = new Subject(); subject.subscribe(data => console.log(data)); subject.next('hello'); // 将会打印出 'hello' subject.next('world'); // 将会打印出 'world'
- 引用计数
当 Subject 实例作为 Observer 来订阅其它 Observable 或 Subject 时,每当被它订阅的 Observable 或 Subject 调用 .complete() 或 .error() 时,Subject 实例就会取消订阅。相当于一个引用计数,当计数为 0 时,Subject 实例也将成为冷 Observable,不再发射值。
使用场景
Subject 为什么这么重要?什么时候应该使用它呢?
发送值类型的事件
Subject 可以作为一个事件总线来使用,当需要发送值类型的事件时,你可以考虑使用 Subject。例如:
const subject = new Subject(); // 发送事件 subject.next('hello'); subject.next('world'); // 订阅事件 subject.subscribe(data => console.log(data));
这段代码中,Subject 实例可以发送事件,也可以作为事件总线,将一个值类型的事件接受者(即 .subscribe 注册的回调函数)绑定到多个事件源(即 .next 发送事件)。这就大大简化了组件之间的数据交换,并且提高了代码的复用性和可维护性。
充当状态对象
当需要在多个组件中共用一个状态对象时,可以考虑使用 Subject。以下以 Angular 为例:
<!-- 在组件 A 中订阅该状态 --> <ng-container *ngIf="state$ | async as state"> <p>姓名:{{ state.name }}</p> <p>年龄:{{ state.age }}</p> </ng-container> <!-- 在组件 B 中发送该状态 --> <button (click)="updateState()">更新状态</button>
-- -------------------- ---- ------- ------ - ---------- - ---- ---------------- ------ - --------------- - ---- ------- ------------- ----------- ------- -- ------ ----- ------------ - ------- ------------ - --- ----------------- ----- --- ---- - --- ------ - --------------------------------- ----------------- ------- ---- ------- - ------------------------ ----- --- --- - -
以上代码中,我们使用 BehaviorSubject(继承了 Subject 的行为)来保存状态,使用它的好处是:
组件在订阅状态时,可以用 | async 管道简化模板代码,同时省去 unsubscribe 操作;
多次订阅时,始终可以获取到最新的状态,并且可以通过 .next() 方法来更新该状态。
作为组件之间的数据传递机制
当需要在组件之间传递数据时,可以考虑使用 Subject。例如:
<!-- 在组件 A 中发送通知 --> <button (click)="notification()">发送通知</button> <!-- 在组件 B 中订阅通知 --> <ng-container *ngIf="message$ | async as message"> <p>{{ message }}</p> </ng-container>
-- -------------------- ---- ------- ------ - ---------- - ---- ---------------- ------ - ------- - ---- ------- ------------- ----------- ------- -- ------ ----- ------------------- - ------- -------------- - --- ------------------ -------- - ----------------------------------- -------------- - ------------------------------------ - -
以上代码中,我们创建了一个全局的 NotificationService,以 Subject 作为数据传递机制。组件 A 可以通过 notification() 方法来发送通知,同时组件 B 可以通过订阅 message$ 来接收通知。这种方式可以很方便地解决组件之间的数据传递问题,而且代码结构也很清晰。
代码示例
以下是一个使用 Subject 实现组件之间的通信的示例代码:
-- -------------------- ---- ------- ------ - --------- - ---- ---------------- ------ - -------------- - ---- -------------------- ------------ --------- ------------- --------- - ------------- ------ ------ --------- ----------- -- ------- --------------------------------------------------- -- -- ------ ----- --------------- - ------------------- --------------- --------------- -- -------------------- ------- - ----------------------------------------- - - ------------ --------- ------------ --------- - ------------- ------ ----- ------- ------ -- -- ------ ----- -------------- - -------- ------- ------------------- --------------- --------------- -- ---------- - -------------------------------------------------- -- - ------------ - -------- --- - - ------------- ----------- ------- -- ------ ----- -------------- - ------- ------- - --- ------------------ -------------------- ------- - --------------------------- - ------------ - ------ ---------------------------- - -
以上代码中,我们定义了 ParentComponent 和 ChildComponent 两个组件,它们之间通过 SubjectService 来传递消息。在 ParentComponent 中,我们通过 sendMessage() 方法来发送消息;在 ChildComponent 中,我们通过 getMessage() 方法来订阅消息,当有新的消息时,则会将该消息渲染到模板中。最后,我们将 SubjectService 定义为可注入的服务,以便在多个组件中重复使用。
总结
Subject 是 RxJS 中非常重要的概念之一,它同时具有 Observable 和 Observer 的能力,可以作为数据总线、状态对象、数据传递机制等多种用途。在实际的开发中,有时会遇到多个组件之间需要共享状态或传递数据的情况,这时使用 Subject 会是一种非常方便的解决方案。当然了,需要注意的是,在使用 Subject 时也要注意避免内存泄漏等问题。如果你还没有使用过 RxJS 中的 Subject,可以参照本文中的示例代码尝试一下。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64abcf3348841e98947a68bb