angular2中数据状态管理方案有哪些?
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。
为什么有这个结论可以移步
https://www.youtube.com/watch?v=jvKGQSFQf10对于不想看英文视频的同学,我来搬运下视频的主要内容:
1. Change Detection
angular 使用了
https://github.com/angular/zone.js来监视 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 应用中保持数据的同步更新? - 太狼的回答。
参考资料: