背景
最近在跟老师做项目的时候,需要处理这样一种情况:同一组数据需要在多个组件中使用或显示出来,其中一个组件有修改(增删改)该组数据的功能,且修改后所有组件中的该组数据需要同步更新。
其实,不使用状态管理也可以实现该功能,但却是要繁琐很多。用上了状态管理则省去了很多不必要的代码。
什么是状态管理
状态管理是指有效地管理应用程序中的,可以共同访问或共享的数据的过程。简单来说就是将程序中各个部分(多个组件)共同使用的数据抽象成一个状态,并再此基础上进行数据的管理。
我个人认为状态管理中最重要的一点是:实现了各个组件对状态的订阅,当状态被更新时,会向订阅了该状态的各组件发送更新后的状态(数据)。
有关状态管理的更多原理及细节,可以看之前学长的文章。
为什么使用状态管理
就上述背景中描述的问题而言,我们可以先来看看不使用状态管理与使用状态管理在思路上的区别。
不使用状态管理:下图中的组件1具有修改功能。
使用状态管理:下图中的组件1具有修改功能,组件2为首次请求数据的组件。
我们可以看到,如果不使用状态管理,当一个组件中对共用数据进行操作后,需要通过组件间的通信(父子组件、同级组件)向其他组件发起通知,告知它们数据有所变更,请重新拉取。
而使用状态管理,则完全不用这样,直接订阅即可。当组件数量多时,这无疑减少了大量不必要的组件间的通信。
使用 @tethys/store 进行状态管理
一个小型且强大的 Angular 状态管理类库。
更多详情请看官方文档。
接下来主要是介绍与记录它的一般使用方法。
安装
官方文档给出的安装方式:
npm i @tethys/store --save
定义状态 Status 并在 Service 上继承
通常我们一般会在service.ts文件中定义 Status,如在 store.service.ts 中:
interface UserStatus extends Store<User> {
user: User
count: number;
}
然后直接继承:
export class StoreService extends Store<UserStatus>
继承后需要在构造函数中初始化 UserStatus 的某些值:
constructor() {
super({
count: 0
});
}
用 super()
初始化需要的数据,当然,如果不需要初始化可以传入一个空对象,向这样:
constructor() {
super({});
}
上述代码中我定义了状态 UserStatus 并继承了自己业务所需要的实体 User :
export interface User {
/** id*/
id: number;
/** 姓名*/
name: string;
/** 性别*/
sex: number;
/** 联系电话*/
phone: string;
}
并在初始化时设置了 UserStatus 中的 count 值为 0。
设置函数与 @Action()
根据官方文档,我们需要在函数前加上 @Action 注解。像这样:
@Action()
addCount() {
return of(true).pipe(
tap(() => {
this.update({
count: this.snapshot.count + 1
});
})
);
}
@Action()
reduceCount() {
return of(true).pipe(
tap(() => {
this.update({
count: this.snapshot.count - 1
});
})
);
}
@Action()
changeUser(key: number) {
return of(true).pipe(
tap(() => {
let name = '';
let sex = 0;
let phone = '';
if (key % 3 === 0) {
name = 'zhangsan';
sex = 0;
phone = '13000000000';
}
if (key % 3 === 1) {
name = 'lisi';
sex = 1;
phone = '1311111111';
}
if (key % 3 === 2) {
name = 'wangwu';
sex = 0;
phone = '13222222222';
}
this.update({
user: {
id: 2,
name: name,
sex: sex,
phone: phone
},
});
})
);
}
上述代码中的 this.update()
方法,就是用来更新我们的状态的。
还有 this.snapshot
则是当前状态的快照,里面是当前状态各个属性的值。
定义静态方法以供 C 层通过 select() 订阅状态或数据
// 只订阅user
static user(status: UserStatus) {
return status.user;
}
// 只订阅count
static count(status: UserStatus) {
return status.count;
}
// 订阅整个状态
static allStore(status: UserStatus) {
return status;
}
至此,store.service.ts 中已经有了我们需要的所有内容,接下来只需要去到各个组件的 C 层中去订阅状态和调用方法即可。
组件中订阅状态和调用方法
C 层构造函数中注入 Service 后,调用方法的方式并没有什么变化,而订阅的方式则需要通过 .select(Service.staticMethod).subscribe()
的方式进行订阅。
store.component.ts中:
constructor(public storeService: StoreService) {}
ngOnInit(): void {
this.storeService.select(StoreService.allStore)
.subscribe(allStore => {
this.currentUser = allStore.user;
this.count = allStore.count;
});
}
changeUser() {
this.key++;
this.storeService.changeUser(this.key);
}
store2.component.ts中:
constructor(public storeService: StoreService) {}
ngOnInit(): void {
this.storeService.select(StoreService.allStore)
.subscribe(allStore => {
this.currentUser = allStore.user;
this.count = allStore.count;
});
}
increase() {
this.storeService.addCount();
}
decrease() {
this.storeService.reduceCount();
}
store3.component.ts中:
constructor(public storeService: StoreService) {}
ngOnInit(): void {
this.storeService.select(StoreService.user)
.subscribe(user => {
this.currentUser = user;
});
this.storeService.select(StoreService.count)
.subscribe(count => {
this.count = count;
});
}
我们可以看到,上述有3个组件共享 service 中的状态 UserStatus 。其中 store 负责修改 UserStatus 中的 user ;store2 负责修改 UserStatus 中的 count ;store3 则只订阅不负责修改。运行后看看效果。
效果预览
前文的代码中,我们并没有去做组件中的通信内容。使用状态管理的方式实现了当数据修改时,其他使用了该数据的组件对该数据的同步更新。
希望这篇文章对你有所帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。