3

背景

最近在跟老师做项目的时候,需要处理这样一种情况:同一组数据需要在多个组件中使用或显示出来,其中一个组件有修改(增删改)该组数据的功能,且修改后所有组件中的该组数据需要同步更新。

其实,不使用状态管理也可以实现该功能,但却是要繁琐很多。用上了状态管理则省去了很多不必要的代码。

什么是状态管理

状态管理是指有效地管理应用程序中的,可以共同访问或共享的数据的过程。简单来说就是将程序中各个部分(多个组件)共同使用的数据抽象成一个状态,并再此基础上进行数据的管理。

我个人认为状态管理中最重要的一点是:实现了各个组件对状态的订阅,当状态被更新时,会向订阅了该状态的各组件发送更新后的状态(数据)。

有关状态管理的更多原理及细节,可以看之前学长的文章

为什么使用状态管理

就上述背景中描述的问题而言,我们可以先来看看不使用状态管理与使用状态管理在思路上的区别。

不使用状态管理:下图中的组件1具有修改功能。
image.png

使用状态管理:下图中的组件1具有修改功能,组件2为首次请求数据的组件。
image.png

我们可以看到,如果不使用状态管理,当一个组件中对共用数据进行操作后,需要通过组件间的通信(父子组件、同级组件)向其他组件发起通知,告知它们数据有所变更,请重新拉取。

而使用状态管理,则完全不用这样,直接订阅即可。当组件数量多时,这无疑减少了大量不必要的组件间的通信。

使用 @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 则只订阅不负责修改。运行后看看效果。

效果预览

store.gif

前文的代码中,我们并没有去做组件中的通信内容。使用状态管理的方式实现了当数据修改时,其他使用了该数据的组件对该数据的同步更新。

希望这篇文章对你有所帮助。

参考资料

https://worktile.github.io/store/guides/getting-started

https://segmentfault.com/a/1190000043520667


HHepan
164 声望13 粉丝

河北工业大学