一、redux
redux⽤于状态集中存储,状态的更新必须是⼀种可预测的⽅式更新。严格的单向数据流是Redux架构的设计核⼼。 这意味着应⽤中所有的数据都遵循相同的⽣命周期,这样可以让应⽤变得更加可预测且容易理解。
什么时候用redux?
- 多交互,多数据源等场景
- 某个组件的状态,需要共享
- 某个组件状态需要在任何地⽅都可以拿到
- ⼀个组件需要改变全局状态
- ⼀个组件需要改变另⼀个组件的状态
Redux之类的状态管 理库充当了⼀个应⽤的业务模型层,并不会受限于如React之类的View层。
1、redux三大原则 - 单⼀数据源整个应⽤的state 被储存在⼀棵object tree中,并且这个object tree只存在于唯⼀⼀个 store中。这让同构应⽤开发变得⾮常容易。来⾃服务端的state可以在⽆需编写更多代码的情况下被 序列化并注⼊到客户端中。由于是单⼀的 state tree,调试也变得⾮常容易。在开发中,你可以把应 ⽤的state保存在本地,从⽽加快开发速度。此外,受益于单⼀的state tree ,以前难以实现的如“撤 销/重做”这类功能也变得轻⽽易举。
- State是只读的唯⼀改变state的⽅法就是触发action,action 是⼀个⽤于描述已发⽣事件的普通对 象。 这样确保了视图和⽹络请求都不能直接修改state,相反它们只能表达想要修改的意图。因为所 有的修改都被集中化处理,且严格按照⼀个接⼀个的顺序执⾏,因此不⽤担⼼竞争条件(race condition)的出现。 action 就是普通对象⽽已,因此它们可以被⽇志打印、序列化、储存、后期调 试或测试时回放出来。
- 使⽤纯函数来执⾏修改为了描述action 如何改变state tree ,你需要编写reducers。 Reducer只是⼀ 些纯函数,它接收先前的state和action,并返回新的state。reducer主要是为了约束修改行为的,不让随便修改。刚开始你可以只有⼀个reducer,随着 应⽤变⼤,你可以把它拆成多个⼩的reducers,分别独⽴地操作state tree的不同部分,因为reducer 只是函数,你可以控制它们被调⽤的顺序,传⼊附加数据,甚⾄编写可复⽤的reducer来处理⼀些通 ⽤任务,如分⻚器。
redux使用示例:
import {
createStore } from 'redux';
/**
* 这是一个 reducer,形式为 (state, action) => state 的纯函数。
* 描述了 action 如何把 state 转变成下一个 state。
*
* state 的形式取决于你,可以是基本类型、数组、对象、
* 甚至是 Immutable.js 生成的数据结构。惟一的要点是
* 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
*
* 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
* 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);
// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
console.log(store.getState())
);
// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({
type: 'INCREMENT' });
// 1
store.dispatch({
type: 'INCREMENT' });
// 2
store.dispatch({
type: 'DECREMENT' });
// 1
createStore:
Redux主要由三部分组成:store、reducer、action。Redux的核⼼就是 store,它有Redux提供的createStore函数⽣成,该函数返回3个处理函数getState,dispatch,subscribe。
export default function createStore() {
let state = {
} // 公共状态
const getState = () => {
} // 存储的数据,状态树;
const dispatch = () => {
} // 分发action,并返回⼀个action,这是唯⼀能改变
store中数据的⽅式;
const subscribe = () => {
} //注册⼀个监听者,store发⽣变化的时候被调⽤。
return {
dispatch,
subscribe,
getState
}
}
getState:
getState()实现 对象包含所有数据。如果想得到某个时点的数据,就要对Store⽣成快照。这种时点的数 据集合,就叫做State。
const getState = () => {
return state; }
dispatch
dispatch()的实现 直接修改state,state.num+‘a’,如修改state像这种情况,就会导致结果不是我们想要 的,后果可能很严重,如果避免呢? 如果可以随意修改state,会造成难以复现的bug ,我们需要实现有条件并且是具名修改的store数据,既然要分发action这⾥要传⼊⼀个action对象,另外这对象包括我们 要修改的state和要操作的具名actionType,这⾥⽤type属性值的不同来对state做相应的修改,代码如
下:
const dispatch = (action) => {
switch (action.type) {
case 'ADD':
return {
...state,
num: state.num + 1,
}
case 'MINUS':
return {
...state,
num: state.num - 1,
}
case 'CHANGE_NUM':
return {
...state,
num: state.num + action.val,
} // 没有匹配到的⽅法 就返回默认的值
default:
return state
}
}
从代码上看,这⾥并没有把action独⽴出来,接着往下看吧。 函数负责⽣成State。由于整个应⽤只有⼀ 个 State对象,包含所有数据,对于⼤型应⽤来说,这个State必然⼗分庞⼤,导致 Reducer函数也⼗分 庞⼤。
reducer:
reducer是⼀个纯函数,它根据action和initState计算出新的state。
强制使⽤action来描述所有变化带来的好处是可以清晰地知道应⽤中到底发⽣了什么。如果⼀些东⻄改 变了,就可以知道为什么变。最后,为了把action和state串起来,就有了reducer。 reducer.js:
export default function reducer(action, state) {
//通过传进来的 action.type 让管理者去匹配要做的事情
switch (action.type) {
case 'ADD':
return {
...state,
num: state.num + 1,
}
case 'MINUS':
return {
...state,
num: state.num - 1,
}
case 'CHANGE_NUM':
return {
...state,
num: state.num + action.val,
} // 没有匹配到的⽅法 就返回默认的值
default:
return state
}
}
action:
Action是把数据从应⽤(译者注:这⾥之所以不叫view是因为这些数据有可能是服务器响应,⽤户输⼊ 或其它⾮view的数据 )传到 store 的有效载荷。它是 store 数据的唯⼀来源。⼀般来说你会通过
store.dispatch() 将 action 传到store。
可以这样理解,Action 描述当前发⽣的事情。改变State 的唯⼀办法,就是使⽤ Action。它会运送数据到 Store。
export const ADD = 'ADD'
export const MINUS = 'MINUS'
export const CHANGE_NUM = 'CHANGE_NUM'
/* * Action Creator 来⽣成action */
export function add(