Angular 状态管理方案调研
1 / 状态管理
Rxjs + Service 组件内管理状态:在组件中可以声明一个属性,作为组件的内存存储。每次操作时调用服务(service)中的方法,然后手动更新状态。
export class TodoComponent {
todos : Todo[] = []; // 在组件中建立一个内存TodoList数组
constructor(
@Inject('todoService') private service,
) {}
addTodo(){
this.service
.addTodo('test') // 通过服务新增数据到服务器数据库
.then(todo => { // 更新todos的状态
this.todos.push(todo); // 使用了可改变的数组操作方式
});
}
}
Rxjs + Service 组件只需访问,状态在服务中存储管理:在服务中定义一个内存存储,然后在更新服务数据后手动更新内存存储,组件中只需要访问该属性即可。
export class TodoService {
private _todos: BehaviorSubject;
private dataStore: { // 我们自己实现的内存数据存储
todos: Todo[]
};
constructor() {
this.dataStore = { todos: [] };
this._todos = new BehaviorSubject([]);
}
get todos(){
return this._todos.asObservable();
}
addTodo(desc:string){
let todoToAdd = {};
this.http
.post(...)
.map(res => res.json() as Todo) //通过服务新增数据到服务器数据库
.subscribe(todo => {
this.dataStore.todos = [...this.dataStore.todos, todo];
//推送给订阅者新的内存存储数据
this._todos.next(Object.assign({}, this.dataStore).todos);
});
}
}
类 Redux 管理方案- ngrx & ngxs
其他未调研产品- Akita & mobX & Redux & Flux
2 / ngrx
ngrx/store的灵感来源于Redux,是一款集成RxJS的Angular状态管理库,由Angular的布道者Rob Wormald开发。它和Redux的核心思想相同,但使用RxJS实现观察者模式。它遵循Redux核心原则,但专门为Angular而设计。
基本原则/概念
State(状态) 是指单一不可变数据
Action(行为) 描述状态的变化
Reducer(归约器/归约函数) 根据先前状态以及当前行为来计算出新的状态
状态用State的可观察对象,Action的观察者——Store来访问
Actions- Actions是信息的载体,它发送数据到reducer,然后reducer更新store。Actions是store能接受数据的唯一方式。在ngrx/store里,Action的 接口 是这样的:
export interface Action {
type: string;
payload?: any;
}
Reducers- Reducers规定了行为对应的具体状态变化。它是纯函数,通过接收前一个状态和派发行为返回新对象作为下一个状态的方式来改变状态,新对象通常用Object.assign和扩展语法来实现。
export const todoReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_TODO':
return [...state, action.payload];
default:
return state;
}
}
Store - store中储存了应用中所有的不可变状态。ngrx/store中的store是RxJS状态的 可观察对象 ,以及行为的 观察者 。我们可以利用Store来派发行为。当然,我们也可以用Store的select()方法获取可观察对象,然后订阅观察,在状态变化之后做出反应。
Selector - 可见示例代码
Effects- Redux 中的 Reducer 已经是一个纯函数,而且是完全的只对状态数据进行处理的纯函数。在发出某个 Action 之后,Reducer 会对状态数据进行处理然后返回。但一般来说,其实在执行 Action 后我们还是经常会可以称为 Effect 的动作,比如:进行 HTTP 请求,导航,写文件等等。而这些事情恰恰是 Redux 本身无法解决的,@ngrx/effects 用于解决这类场景,一个 http 请求的示例如下 https://gist.github.com/hijiangtao/d4def77867ff4aec2740ba6ab83b24bf
@Component({
template: `
<div *ngFor="let movie of movies$ | async">
</div>
`
})
export class MoviesPageComponent {
movies$: Observable<Movie[]> = this.store.select(state => state.movies);
constructor(private store: Store<{ movies: Movie[] }>) {}
ngOnInit() {
this.store.dispatch({ type: '[Movies Page] Load Movies' });
}
}
最佳实践
根 store 模块 - 创建根 store 模块作为一个完整的 Angular 模块,与 NgRx 的 store 逻辑绑定在一起。功能 store 模块将被导入到根 store 中,这样唯一的根 store 模块将被导入到应用程序的主 App Module 模块中。
创建功能 store 模块
方式一:Entity Feature Module - 定义 actions / 创建 state / 创建 reducer / 创建 selector / 创建 effects
方式二:标准的功能模块 - 同上
模块导入 angular - app.module.ts 引入
优势
豌豆资源搜索网站https://55wd.com 广州vi设计公司http://www.maiqicn.com
中心化,状态不可变 - 所有相关应用程序的状态都缓存在一个位置。这样可以很容易地跟踪问题,因为错误时的状态快照可以提供重要的见解,并且可以轻松的重新重现这个问题。这也使得众多困难问题,例如在Store应用程序的上下文中撤消/重做某一步骤,并且实现了更强大的功能的工具。
性能 - 由于状态集合中应用程序的顶层,因为数据更新可以通过组件依赖于Store。Angular构建如这样的数据流布置进行优化,并且可以在组件依赖于没有发布新值的Observables的情况下禁用变化检测。
测试 - 所有状态更新都是在recudes中处理的,它们是纯函数。纯函数测试非常简单,因为它只是输入,反对输出。这样可以测试应用程序中最关键的方面,而无需使用mock,或其他的测试技巧,可以使测试复杂且容易出错。