下载APP

使用Angular服务Service+Rxjs实现Angular的状态管理


theme: cyanosis highlight: a11y-dark

背景:最近做项目中遇到多个组件共用一个后端接口数据。想着在每个组件中使用请求比较麻烦,就基于service写了一个【又不是不能用.jpg】的store。

p34066865.jpg

在Angular官网的英雄之路教程中,有说过

组件不应该直接获取或保存数据。 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。

我的理解就是组件聚焦于与页面的互动,service聚焦于与后端数据库的交互。

目标:实现一个store,父子组件共享该store的数据,相互影响。

1.新建一个service

image.png

定义store对象

export class RxjsReduxService {

  private store = {
    list:[1,2]
  }

将store定义为private,防止直接访问

2.首先,我要让组件可以去访问这个store

这里使用rxjs建立一个简单的发布者去发布该store,而组件通过订阅该store实现对store对象的访问

RxjsReduxService.ts
    BehaviorStore$: any = new BehaviorSubject(this.store);

以上已经简单实现了一个可供访问的store,在组件中,可以通过this.rxjsReduxService.BehaviorStore$.subscribe()实现对store的订阅。但是这个简单的实现并不能满足要求,主要有两点

  1. BehaviorStore$直接暴露给订阅者,订阅者可以通过this.rxjsReduxService.BehaviorStore$.next()直接去发布消息,影响其它订阅者。
  2. 从项目业务需求看,订阅者直接调用next也许是一种更快捷的组件通信方式,但是随着项目的复杂化,这种通信变得难以维护,我们的目标是实现状态的管理,而不是将这种状态复杂化。

3.继续优化

优化目标是:隐藏next

实现:使用Subject.prototype.asObservable()这样做的目的是为了防止主体的“观察者端”从API中泄漏出来。意思就是只能在service中统一去用next发布。代码如下:

  private BehaviorStore$: any = new BehaviorSubject(this.store);

  get store$ (){
    return this.BehaviorStore$.asObservable().pipe(shareReplay(1));
  }

首先通过private关键字将BehaviorStore$隐藏,再新建一个store$暴露给订阅者。

在组件中注入service并订阅该store

app.component.ts
export class AppComponent {
  data:any
  constructor(private rxjsReduxService:RxjsReduxService) {
    this.rxjsReduxService.store$.subscribe((res:any) => {
      this.data = res
    })
  }

4.通过commit方法去修改store中的内容,并通知给所有订阅者

首先,先确认调用的方式,再去实现

app.component.ts
  updata(){
  this.rxjsReduxService.commit({type:'addState',payload[Math.random()*10]})
  }
  1. commit方法中传入的是一个对象
  2. type属性代表action类型,payload属性是传的值

开始实现commit

4.1新建一个reducer.ts按照不同的action类型,集中对store进行操作

reducer.ts
import { of } from "rxjs"

export const reducer = (currentStore:any,action:any) => {
    switch(action.type){
        case 'addState':
            const newStore = {
                ...currentStore,
                list:[...currentStore.list,...action.payload]
            }
            //将参数转化成一个 Observable 对象。
            return of(newStore)
    }
    return of(currentStore)
}

如上,currentStore代表当前的store对象,如果action的类型是addState,则将payload中的值加入到当前的store对象中。并返回一个Obeservable对象。

不能忘记,我们调用的是commit方法,调用的目的是1.改变当前store;2.当前store变化后应及时推送给所有的订阅者,此时,目标1已经实现,再来实现目标2。

4.1 不难想出,目标2不就是再将改变后的store重新next出去吗?实现以下。

在RxjsReduxService.ts中定义commit方法

  commit = (action:any) => {
    from(reducer(this.BehaviorStore$.value,action)).subscribe({
      next:(data) => this.BehaviorStore$.next(data),
      error:(data) => this.BehaviorStore$.error(data)
    })
  }

在该commit方法中,1.调用了reducer方法来对store进行操作;2.通过next将最新的store推送出去,此时,该store的订阅者都能够收到最新的store信息。

5.总结

1.上述方案并不完善,只是做项目的一点思考。
2.充斥着大量的anyscript,仅仅只是一个实例代码,项目中原始store数据应该来自后端,此时在constructor中可以进行一次next推送。
3. rxjs的介绍不足,可以自行学习。
4. 又不是不能用.jpg。
在线举报