Angular 中的状态管理实践

754 阅读3分钟

使用 Component 拆分界面

我们知道,Angular 中的 component 是 TS class, html, css 三个文件组成的,抛开 css 不谈,我们可以把 html 当作,view 层,class 当作逻辑层。整个页面可以简单的通过 component 组成的逻辑树渲染而成。如类似如下的结构:

app 
    --header
    --body
      --body.html
      --body.ts

当然,我们可以把共享的逻辑拆分成更下的 component。比如,A 页面, B 页面 共享一个 Search Box, 那我们就可以将 Search Box 抽离成一个独立的 searchBox.component。

使用 Loc Service 拆分 UI 无关的逻辑代码

但其实有时候,并不一定是 UI 与逻辑都恰好是共享的逻辑块,有时候,会有一部分纯逻辑,需要被共享,我们需要单独抽离开来,Angular 中为我们提供了 Service 这样的概念。比如,A,B component 同时需要使用 c.service。我们就可以非常简单的使用依赖注入实现:

class A {
  constructor(
        public cService: CService,
    ) { }
}

class B {
  constructor(
        public cService: CService,
    ) { }
}

CService 会在第一次使用的时候被实例化,比如,如果 A component 被先创建的话,cService = new CService(), 就已经被调用的, B Component 再被创建的话,就会直接使用已经被创建好的 cService。当然,你也可以手动指定当前的 cService 是随着 component 新创建的。通过指定,provides 就可以实现。

@Component({
    ////////
    providers: [CService],
})
export class A {
    constructor(
            public cService: CService,
        ) { }
}

这样做有什么好处呢?我们来看一下component中的例子,HTML 中需要显示,fullName,我们会在 class 中定义 fullName, 这样通过藏检查,只要 ts 中的 fullName 变了,HTML 中的 fullName 也会重新改变。

<div>{{fullName}}</div>

//////
export class A {
    fullName = 'zhangsan'
    constructor(
            public cService: CService,
        ) { }
}

但是实际情况中,也可能会复杂的多,TS 中可能会有大量的逻辑部分,这部分逻辑与 View 并没有直接联系。举个例子:

export class A {
    fullName = 'zhangsan';
    firstName;
    lastName;
    constructor(
            public cService: CService,
        ) { }
        
    getFirstName() {}
    getLastName() {}
}

FullName 可能是通过复杂的计算得来的,而我们实际上再 Component 这一层只关心 FullName 的变化,对中间层并不关心。很容易想到,我们其实应该将具体的逻辑层抽离出来,哪怕这部分逻辑并不会被共享。这样我们就用到了刚刚的 Service 的用法。这样,我们对于一个常见的复杂 component, 基本上得到了这样一个编程范式:

HTML + Component.ts
TS class 通过 Loc 调用 service。

ts class 中的 public property 与 HTML 中使用的变量对应。(应该尽可能减少 HTML 中不使用的变量,避免逻辑变得复杂)

Service 分为两种:一种是共享的 service,一种是只属于当前 class 的 service。通过 service 组成 component 中想要使用的数据。

Store

Service 的概念很简单,就是通过 Loc 被使用的 class,但是在 component 的使用中,有一种范式很被常用:

  • class 提供 state
  • class 提供 methods 修改 state
  • state 只能被 methods 修改,不能直接被修改
  • state 可以被监听变化 这种 class 的范式,我们可以称之为 store。

我们来看如何简单的实现一个 store:

class AStore {
  state = {
     a1: '',
     a2: ''
  };
  
  changeA1 = (a) => {
    return ({
      ...this.state,
      a1: a,
    });
  }
}

当然,如果把 State 包裹为一个 Observable 就可以不依赖藏检查。

这样,因为 store 是直接被 Component 使用的,我们就可以得到这样一个编程范式:

HTML Template => Component => Store (local store, shared store) => Service

当然 store 也可以 call store, service 也可以 call service,但是层数不应该太深。

NgRx Store

NgRx 帮我们提供了 NgRx store 和 NgRx component store。其实,component store可以理解成 local store,NgRx store 可以当成 shared store。正常的范式可以是

component => component store => ngrx store
component => component store + ngrx store