Angular2 之 @ngrx/store
RxJs驱动Angular应用程序的状态管理,其灵感来自于Redux。
什么是Redux?Redux是为了解决应用程序状态(State)管理而提出的一种解决方案。那么什么是状态呢?简单来说对于应用开发来讲,UI上显示的数据、控件状态、登陆状态、数据加载画面的不同状态等等全部可以看作状态。 如果应用程序中没有明确的状态的管理,很多项目随着需求的迭代,代码规模逐渐扩大、团队人员水平参差不齐就会遇到各种状态管理极其混乱,导致代码的可维护性和扩展性降低。 那么Redux是怎么解决这个问题的呢?它提出了几个概念:Reducer、Action、Store。 Store可以把Store想象成一个数据库,就像我们平时在开发过程中使用的Oracle数据库一样,Store是一个你应用内的数据(状态)中心。Store在Redux中有一个基本原则:它是一个唯一的、状态不可修改的树,状态的更新只能通过显性定义的Action发送后触发。 Store中一般负责:保存应用状态、提供访问状态的方法、派发Action的方法以及对于状态订阅者的注册和取消等。 遵守这个约定的话,任何时间点的Store的快照都可以提供一个完整当时的应用状态。 Reducer我一直觉得Reducer这个单词不好理解,它的英文解释有:减速器、还原剂还有一个是异形接头。这几个单词的意思,我都不能直接拿过来用。 还好今天看到这边文章,文章中如下描述:
我觉得这个描述非常好,因为数组的reduce操作就是给出不断的用序列中的值经过累加器计算得到新的值,这和旧状态进入reducer经处理返回新状态是一样的。 很多网上的文章说可以把Reducer想象成数据库中的表,也就是Store是数据库,而一个reducer就是其中一张表。我其实觉得Reducer不太像表,我还是觉得这个累加器我比较好理解。 Reducer其实就是用来维护状态的 Reducer是一个纯javascript函数,接收2个参数:第一个是处理之前的状态,第二个是一个可能携带数据的动作(Action)。 export interface Reducer<T> { (state: T,action: Action): T; }
那么纯函数是意味着什么呢?意味着我们理论上可以把reducer移植到所有支持Redux的框架上,不用做改动。 什么是纯函数呢?
下面我们来看一段简单的代码: export const counter: Reducer<number> = (state = 0,action) => {
switch(action.type){
case 'INCREMENT': return state + action.payload;
case 'DECREMENT': return state - action.payload;
default: return state; }};
上面的代码定义了一个计数器的Reducer,一开始的状态初始值为0((state = 0,action) 中的 state=0 给state赋了一个初始状态值)根据Action类型的不同返回不同的状态。这段代码就是非常简单的javascript,不依赖任何框架,可以在React中使用,也可以在接下来的我们要学习的Angular2中使用。 ActionStore中存储了我们的应用状态,Reducer接收之前的状态并输出新状态,但是我们如何让Reducer和Store之间通信呢?这就是Action的职责所在。在Redux规范中,所有的会引发状态更新的交互行为都必须通过一个显性定义的Action来进行。 下面的示意图描述了如果使用上面代码的Reducer,显性定义一个Action{type: ‘INCREMENT’,payload: 2}并且dispatch这个Action后的流程。 比如说之前的计数器状态是1,我们派送这个Action后,reducer接收到之前的状态1作为第一个参数,这个Action作为第二个参数。在Switch分支中走的是INCRMENT这个流程,也就是state+action.payload,输出的新状态为3.这个状态保存到Store中。 注意: export interface Action {
type: string;
payload?: any; // 这个值可有可没有
}
为什么要在Angular2中使用?angular1.x中没有一个统一的状态管理机制,所以造成状态很混乱。 所以我们要在Angular2中使用状态管理。 在Angular 2中使用Redux
简介@ngrx/store是一个旨在提高写性能的控制状态的容器,在angular的应用程序上是一致的。核心原理有:
这些核心原则支持构建组件,这些组件可以使用 We will use @ngrx/store to provide use with a store for us to well… store our state. 我们使用 要重申,redux中最重要的一个概念是应用程序的整个状态都集中在一个JavaScript对象树中。 因为我们的对象状态树是只读的,我们对每个动作的响应必须返回一个新的状态对象,而不改变先前的状态对象。在实现redux时,在reducer中实现不可变性是至关重要的,因此将逐步介绍下面的每个操作,并讨论如何完成这一任务。 知识点
一个简单的使用例子在您的应用程序创建一个Reducer函数为每个数据类型。这些Reducer将使您的应用程序的组合状态: // counter.ts
import { ActionReducer,Action } from '@ngrx/store';
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const RESET = 'RESET';
export function counterReducer(state: number = 0,action: Action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
// 这是一个例子:添加一个新的元素
case 'ADD_TODO':
return [ ...state,action.payload ];
default:
return state;
}
}
逻辑其实就是列表数组增加一个元素,用数组的push方法直接做是不是就行了呢?不行,因为Redux的约定是必须返回一个新状态,而不是更新原来的状态。而push方法其实是更新原来的数组,而我们需要返回一个新的数组。感谢ES7的Object Spread操作符,它可以让我们非常方便的返回一个新的数组。 在APP的主模块中,导入 import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter';
@NgModule({
imports: [
BrowserModule,StoreModule.provideStore({ counter: counterReducer })
]})
export class AppModule {}
你可以在你的组件中注入 import { Store } from '@ngrx/store';
import { INCREMENT,DECREMENT,RESET } from './counter';
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
interface AppState {
counter: number;
}
@Component({
selector: 'my-app',template: `
<button (click)="increment()">Increment</button>
<div>Current Count: {{ counter | async }}</div>
<button (click)="decrement()">Decrement</button>
<button (click)="reset()">Reset Counter</button>
`
})
export class CounterComponent {
counter: Observable<number>;
constructor(private store: Store<AppState>) {
this.counter = store.select('counter');
}
increment() {
this.store.dispatch({ type: INCREMENT });
}
decrement() {
this.store.dispatch({ type: DECREMENT });
}
reset() {
this.store.dispatch({ type: RESET });
}
}
参考:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |