theme: cyanosis highlight: a11y-dark
背景:最近做项目中遇到多个组件共用一个后端接口数据。想着在每个组件中使用请求比较麻烦,就基于service写了一个【又不是不能用.jpg】的store。
在Angular官网的英雄之路教程中,有说过
组件不应该直接获取或保存数据。 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。
我的理解就是组件聚焦于与页面的互动,service聚焦于与后端数据库的交互。
目标:实现一个store,父子组件共享该store的数据,相互影响。
1.新建一个service
定义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的订阅。但是这个简单的实现并不能满足要求,主要有两点
- 将
BehaviorStore$
直接暴露给订阅者,订阅者可以通过this.rxjsReduxService.BehaviorStore$.next()
直接去发布消息,影响其它订阅者。 - 从项目业务需求看,订阅者直接调用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]})
}
- commit方法中传入的是一个对象
- 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)
})
}