一. 直接管理状态
直接管理状态的方式
从服务中获取数据,然后在组件中渲染数据,通过组件树从上到下传递所有值。使用Angular默认的变更检测机制,每当有任一组件变化时,都会检测整个组件树。
直接管理状态的缺点
- 属性的间接传递
- 重构不灵活
- 状态树和DOM树不匹配
- 应用中到处都是状态
二. Redux
Redux:一种现今十分流行的数据结构。其最大优点可能是它的简单性。如果把Redux剥离的只剩核心代码,其代码行数将不到100行。
Redux Primer-Flux:单向流
核心概念
- 应用的所有数据都存放在一个叫做state的数据结构之中,而state存放在store中。
- 应用从store中读取state
- store永远不会被直接修改
- action描述发生了什么,由用户交互触发
- 通过调用一个叫做reducer的函数来结合旧的state和action创建出新的state
主要元素
store
- 保存应用当前状态的一个实例对象
- 存储的是任何时候应用唯一的状态
- 负责运行reducer然后保存新的state
- 每个store都和一个特定的reducer紧密相关。使用私有变量来存储它
常用方法:createStore subscribe dispatch getState
- createStore: 创建一个store的实例对象;
- getState: 返回当前的state变量;
- dispatch: 接收一个action并把它传给reducer,然后用reducer的返回值来更新state变量的值。注意,该方法没有返回值,是一种“触发并忘记”的策略;
- subscribe: 用此方法来注册一个监听函数;当dispatch被调用时,我们遍历所有的监听器并逐个调用它们,它们会负责通知大家这 个state发生了变化。 返回一个unsubscribe函数。
小例子:
//定义store类
class Store<T> {
private _state: T;
private _listeners: ListenerCallback[] = [];
constructor(
private reducer: Reducer<T>,
initialState: T
) {
this._state = initialState;
}
getState(): T {
return this._state;
}
dispatch(action: Action): void {
this._state = this.reducer(this._state, action);
this._listeners.forEach((listener: ListenerCallback) => listener());
}
subscribe(listener: ListenerCallback): UnsubscribeCallback {
this._listeners.push(listener);
return () => { // returns an "unsubscribe" function
this._listeners = this._listeners.filter(l => l !== listener);
};
} }
//使用store类
let store = new Store<number>(reducer, 0);
console.log(store.getState()); // -> 0
// subscribe
let unsubscribe = store.subscribe(() => {
console.log('subscribed: ', store.getState());
});
store.dispatch({ type: 'INCREMENT' }); // -> subscribed: 1
store.dispatch({ type: 'INCREMENT' }); // -> subscribed: 2
unsubscribe();
store.dispatch({ type: 'DECREMENT' }); // (nothing logged)
// decrement happened, even though we weren't listening for it
console.log(store.getState()); // -> 1
provideStore 提供给我们一个应用存储在应用的生命周期中使用。
action
- 描述发生了什么改变,由用户交互触发
- 结构:{type:string;payload?:any}
- 通常命名是自定义的字符串,建议全部使用大写
- 表示type字符串的变量应声明为static,以便可通过类名直接访问,如
static QUEUE = '[NOW PLAYLIST] QUEUE';
- 做为store的dispatch()的参数被使用
action creators:用于创建action的函数。因为直接使用action做为参数时,dispatch语句都不够优雅,原因有以下两点:
- 不再需要手动转换相应的Action类型。
- 如果终决定要改变payload的格式,我们不用更新任何一处dispatch 语句。
一个简单的actionCreator函数:
updateName(name: string): Action {
return {
type: "UPDATE_NAME",
payload: name
};
}
// usage
this.store.dispatch(updateName("oren"))
reducer
- 不能直接修改state,只能返回一个新的state为什么不能直接修改state
- 必须是一个纯函数,不会使用参数之外的任何数据
注:纯函数在参数不变的情况下,总是会返回同一个值;而且纯函数不会调用任何对外界产生影响的函数。纯函数 - 通常采用switch/case结构,default返回原来的state(当传入一个未知的action时,这确保程序不会报错而我们能得到原始的state值)
- 经常需要将reducer分解成若干个sub-reducer,它们各自管理state树中的一个不同分支,使用combineReducer(),可参考reducer分解
生成新的state通常有四种操作:
- 往数组中添加一项
array.concat()
- 从数组中移除一项
array.slice()
- 添加对象中的键或修改对象中某些键的值
Object.assign({}, state, { name: action.payload })
- 从对象中移除键
Object.assign({},{logged_in:false,name:''})
javascript array方法
一个reducer实例:
export function user (state = {}, action: Action):ActionReducer<IUser> {
switch (action.type) {
case "UPDATE_NAME":
return Object.assign({}, state, { name: action.payload });
case "LOG_OUT":
return Object.assign({}, {
logged_in: false,
name: ''
});
default:
return state;
}