angular2中数据状态管理方案有哪些?

react有redux,vue有vuex,那么angular2中怎么做数据状态管理?
关注者
222
被浏览
23,177

5 个回答

最为正统(也就所谓官方)的方式当然是 Rx。。

可以参考

Managing State in Angular 2 Applications

,理论上 Redux 能实现的功能 Rx 当然也都可以,截取一小段代码作为示例:

function todos(initState: Todo[], actions: Observable<Action>): Observable<Todo[]> {
  return actions.scan((state, action) => {
    if (action instanceof AddTodoAction) {
      const newTodo = {id: action.todoId, text: action.text, completed: false};
      return [...state, newTodo];
    } else {
      return state.map(t => updateTodo(t, action));
    }
  }, initState);
}

也有 Rx 和 Redux 的组合产品,比如

GitHub - ngrx/store: RxJS powered state management for Angular2 apps, inspired by Redux

export const counter:Reducer<number> = (state:number = 0, action:Action) => {
    switch (action.type) {
        case INCREMENT:
            return state + 1;
        case DECREMENT:
            return state - 1;
        case RESET:
            return 0;
        default:
            return state;
    }
}

当然,也有纯粹的 ng2 的 Redux 实现,比如

GitHub - angular-redux/ng2-redux: Angular 2 bindings for Redux

等等。。


总之不外乎是 /R.*?x/ 。。

@Saviio

今天刚好看了一下 angular 2 change detection 的部分,遂尝试回答一下。

先说结论,对性能要求高的场景使用 Rx/Immutable,普通的场景用 POJO。

为什么有这个结论可以移步

youtube.com/watch?

对于不想看英文视频的同学,我来搬运下视频的主要内容:

1. Change Detection

angular 使用了

github.com/angular/zone

来监视 component 的变更,并在需要的时候 render dom。由于 ng 2 的 component 的变更检测永远是从 root 开始,一直到最底层的 component 结束,大概像下图这样(图中 CD 表示 change detection):


所以在一个正常的 ng2 change detection cycle 中,不管哪一个 component 中的 ngZone 捕获到了一个异步事件的发生,就会从根 component 往下遍历,进行变更检测,直到最后一个 component。这个思路其实和 ng 1是非常类似的,只不过 ng 1的变更是双向的,也就是说在 ng 1中任意一个 directive/controller 的 $scope 在 $digest 过程中都可能会引发另一个 $scope 的变更从而让 $digest give up 掉重新再来一次。而在 ng2 的 change detection 中,子 component 如果在 change detection cycle 中有任意变更它的父 component 的行为,都会抛出异常从而终止。也就是说同样是 $digest cycle,在 ng 2 中变成了单向的可预测的:


2. 优化

由上面的部分我们可以知道,任意一次数据变更的代价是 time = C * N

C 表示平均每个绑定到模版的值变更检测所需的时间

N 表示绑定的数量。

在 ng2 中,由于框架已经自动为模版生成的代码做了非常多的优化,即使是在未使用优化过的 model 的情况下都已经可以达到 ng 1脏检测性能的 3-10 倍(同样绑定数量,同样检测次数)。视频中提到了这是因为 ng 2 在生成模版代码时,会动态生成让 js 引擎易于优化的代码,大概原理就是保持每次 check change 前后对象“形状 ”的一致。而如果在性能有瓶颈的地方,可以使用下面两种方式进行高阶优化:

  • Immutable

使用 immutable 的思路是减小 N 的值。我们知道在一个 component 中,绑定到模版上的数据一般都是对象上的属性,而对象的 check change 是非常昂贵的,需要遍历绑定到模版上的每一个值。而如果使用了 immutable,则这个 check 的过程将会非常迅速。并且,额外的好处是,在你告诉了框架你使用了 immutable 之后,当你的值并没有变化时,ng 2 的 change detection cycle 会忽略掉这个 component,如下图:

假设每个 component 都使用了 immutable model ,白色的部分是变更的部分,则在一个 change detection cycle 中只会 recheck & render 白色的部分,从而大大减少处理变更的代价。

你可以通过设置 changeDetection 来告诉框架只有绑定的这个对象引用改变时才处理变动:

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})
class TodoCmp {
  todo: Todo
}

  • Observable

这个通常是使用 Rx ,但也可以是 Backbone/Knockout 等(本人强烈不推荐)。使用这种模式的时候我们面临的情况和使用 immutable 的时候并不一样。使用 immutable 时 change detection cycle 的情况和使用 POJO 时很类似,变更非常自然的从 root component 开始往下,依次检测。而使用 Observable 时,情况就不一样了,它的变更很可能是从一个非常下层的子 component 中开始发生的,比如:


在图中一个子 component 通过 observable 观察到了一次数据的变更。这个时候我们需要告知 ng 2 这个部分发生了变更,它将会把这个 component 与它的父 component 一直到 root component 标记出来,并单独检测这一部分的变更:


我们可以使用同样的方法来设置这种变更检测:

@Component({
  template: '{{counter}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})
class CartBadgeCmp {

  @Input() addItemStream:Observable<any>;
  counter = 0;

  ngOnInit() {
    this.addItemStream.subscribe(() => {
      this.counter++; // application state changed
    })
  }
}

三种方案是可以混用的(其实 immutable 和 observable 在框架看来是一样的)。

比如:

只需要在特定的 component 标记出对应的 changeDetection。


3. 坑

由于程序中的 model 和 component 的结构往往并不是相对应的:

所以在处理 model 的变更时,特别是使用 Rx / Backbone 时,要特别小心避免“级联更新” 也就是所谓的 Cascading Updates , 也就是 Flux/Redux 尝试解决的问题。ng 2 让 component 变更处理变成单向的,这并不意味着我们就可以不考虑 model 数据流的设计,在使用 Observable 模式时尤其需要注意不要产生双向的数据流。backbone 在不经意间就会产生这样的结果

如何在大型 Web 应用中保持数据的同步更新? - 太狼的回答

参考资料: