概述
Redux 是 JavaScript 状态容器,提供可预测化的状态管理方案。其三大原则为:
- 单一数据源 => 整个应用的 state 被储存在一颗 object tree 中,并且这个 object tree只存在于唯一一个 store 中
- state 是只读的 => 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
- 使用纯函数来执行修改 => 为了描述 action 如何改变 state tree,需要编写 reducers
纯函数: 即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用
Redux 的核心:
- store => store 是由 Redux 提供的 createStore 生成的,通过 createStore 方法创建的 store 是一个对象
- dispatch => 调度 state 的更新
- reducer => 处理 state 的更新。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。指定了应用状态的变化如何响应 actions 并发送到 store 的。
- action => action 是把数据从应用传到 store 的有效载荷,它是 store 数据的唯一来源。action 是一个对象,其中包括
type和payload属性。描述变化 - Provider => 全局注册,在
<Provider></Provider>内的组件都可拿到 state - connect => 连接组件和 store
自我实现
代码,这部分主要参照了这个系列的相关文章。相关的逻辑可以参照 commit message
官方文档
- 变化 vs 异步
- React 把处理 state 中数据的问题留给了开发者,Redux -> 处理 state 中的数据
- Redux 试图让 state 的变化变得可预测
- reducer 合成 -> 每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。开发一个函数作为主 reducer,它调用多个子 reducer 分别处理 state 中的一部分数据,然后再把这些数据合成一个大的单一对象。
- combineReducers API
- store 职责:
- 维持应用的 state
- 提供
getState()方法获取 state - 提供
dispatch(action)方法更新 state - 通过
subscribe(listener)注册监视器 - 通过
subscribe(listener)返回的函数注销监听器
源码
类型
-
export type Reducer<S = any, A extends Action = AnyAction> = (state: S | undefined, action: A) => S;
createStore
-
类型:Function
-
参数:
- reducer<Reducer>
- 【可选】preloadedState<PreloadedState<S>> => 初始化
state - 【可选】enhancer<StoreEnhancer<Ext, StateExt>> => 增强器,用来扩展
store的功能
-
返回值:
store<Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext>
源码分析
源码
- 1 - 10:
preloadedState和enhancer不能同时为Function - 12 - 15:如果
preloadedState为Function但是enhancer为undefined那么将preloadedState赋给enhancer,并且preloadedState置为undefined
源码
- 1 - 10:如果
enhancer不为undefined,那么enhancer必须为Function,如果是Function直接返回enhancer(createStore)(reducer, preloadedState)。这个在applyMiddleware时具体讲解
// TODO: 使用一个 enhancer 作为例子
- 12 - 14: 如果
reducer不是Function则抛错
源码
-
currentReducer=> 传入的reducer -
currentState=> preloadedState => undefined | 用户传入的state -
currentListeners: (() => void)[] | null=> [] => 存储更新函数的数组 -
nextListeners=> [] => 下次dispatch将会触发的更新函数数组 -
isDispatching<boolean>=>Lock,当前是否在dispatch
源码
定义 ensureCanMutateNextListeners、getState、subscribe、dispatch、replaceReducer 和 observable 函数
源码
- 2:
dispatch一个ActionTypes.INIT的action=> 使得每个reducer返回初始state - 4 - 11: 创建
store变量并返回
dispatch
dispatch
- 2 - 4:判断
action是否是纯粹的对象。即不是Array|Function|null,isPlainObject 定义 - 6 - 8:
action必须有type - 10 - 12:如果当前正在
dispatch则不能dispatch=> 不可以同时dispatch两个action - 14 - 19:执行
currentReducer,传入currentState和action - 21 - 25:将
nextListeners赋给currentListeners和listeners,之后循环遍历执行listener
最终return action
getState
getState
getState 方法比较简单,就是 return currentState
subscribe
subscribe
- 2 - 8:如果
listener不是Function或者isDispatching === true抛错 - 10:定义🔐
isSubscribed=> 是否已经订阅 - 12: 执行
ensureCanMutateNextListeners函数 - 13:将
listener推入nextListeners - 15:
return unsubscribe函数 - 16 - 20:如果
isSubscribed === false直接返回, 如果isDispatching === true抛错 - 22:关🔐
- 24:执行
ensureCanMutateNextListeners函数 - 25 - 27:在
nextListeners中删除listener函数,并将currentListeners置为null
ensureCanMutateNextListeners
ensureCanMutateNextListeners
对 currentListeners 做了浅拷贝以便于可以在 dispatch 的时候使用 nextListeners 作为临时的 listener。这样可以防止使用者在 dispatch 的时候调用 subscribe/unsubscribe 出现 bug。避免相互影响
combineReducers
- 类型:Function
- 参数:reducers
- 返回值:Function
- 使用
combineReducers例子
源码分析
源码
- 2 - 13:将用户传入的
reducers做一个shallow copy,并且剔除不是Function的reducer。-
finalReducers为有效的reducer -
finalReducerKeys=> 有效reducer的key
-
- 15 - 20:执行
assertReducerShape函数,如有Error将Error赋值给shapeAssertionError - 22:返回
combination函数
assertReducerShape
assertReducerShape
遍历给到的 reducer
- 如果在初始化
action时返回undefined,抛错 - 如果执行一个随机
action时返回undefined,抛错
combination
combination
- 2 - 4:如果
assertReducerShape函数执行时出错,则抛出错误 - 6 - 7: 定义
hasChanged🔐 和nextState变量 - 8 - 19:遍历
finalReducerKeys数组,previousStateForKey = state[key],nextStateForKey = reducer(previousStateForKey, action),如果nextStateForKey为undefined抛错。将reducer计算出来的state存入nextState中。并且判断previousStateForKey和nextStateForKey是否改变了 - 20 - 21:判断
finalReducerKeys和传入state的key的length是否改变。最终return hasChanged ? nextState : state
compose
- 类型:Function
- 参数:Function[]
- 返回值:Function
- 使用
compose例子
源码分析
compose
- 2 - 5:如果数组为空,返回一个空函数
- 7 - 9:如果数组的
length为1,则返回第一个函数 - 11:对函数数组进行
reduce操作compose(increase, square, add) // (...args) => increase(sqpare(add(...args)))
注意:Array.reduce() 的使用 => 回调函数第一次执行时,accumulator 和 currentValue 取值有两种情况:如果调用 reduce() 时提供了 initialValue,accumulator 取值 initialValue,currentValue 取数组中的第一个值;如果没有提供 initialValue,那么 accumulator 取数组中第一个值,currentValue 取数组中的第二个值
Lodash compose
applyMiddleware
- 类型:Function
- 参数:Middleware[]
- 返回值:Function
- Middleware:
({getState}) => next => dispatch=>({getState}) => dispatch => action => any
applyMiddleware的功能:改造dispatch函数,产生真假dispatch,而中间件就是运行在假真(dispatchAndLog假和next真)之间的代码。
源码分析
applyMiddleware
applyMiddleware 函数,直接返回一个函数,我们直接看这个函数即可。这个函数的结构是 (createStore) => (reducer, preloadedState?) => store
- 6:执行
createStore函数,获取store - 7 - 12:定义
dispatch函数 - 14 - 17:定义
middlewareAPI对象,这里middlewareAPI只有两个属性,getState和dispatch - 18:将用户传入的
middleware[]依次执行,并将middlewareAPI作为参数传给各个Middleware,Middleware的例子如下
此时function logger({ getState }) { return next => action => { console.log('will dispatch', action) const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue } }chain的值为Middleware返回值的数组// chain => [middleware1, middleware2, middleware3] next => action => { console.log('will dispatch', action) const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue } - 19:
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)。使用compose函数,此时Middleware中的next === store.dispatch - 21 - 24:
return store,这里的dispatch是 19 行的dispatch
之后看一下 Middleware 使用场所。是作为 applyMiddleware 的参数使用。applyMiddleware 做为 createStore 的第三个参数传入 createStore。
createStore enhancer
此时的 enhancer === applyMiddleware,之后在 createStore 里面直接执行
applyMiddleware,将 createStore 和 reducer + preloadedState 传入 applyMiddleware,即在 applyMiddleware 代码中的第 21 行 - 第 24 行的返回值即为 createStore 的返回值
applyMiddleware 做了什么
- 执行传入的
Middleware,并将state作为参数传入Middleware - 更新
dispatch,这个更新后的dispatch是用户传入的(强化的dispatch),例如上述的logger例子action => { console.log('will dispatch', action) // 此处的 next === dispatch === store.dispatch === createStore(reducer, preloadedState).dispatch const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue }
注意点
-
enhancerstore 的增强器。enhancer是一个高阶函数,返回值是一个经过包装的强化的store。applyMiddleware就是一个enhancer
疑问
Redux 做了什么?
- 存储 state
- 更删改查 state => 可以理解为 state 的变化
- 触发更新
middleware 的执行顺序
Redux 的中间件模型类似于 koa。在 next 前面以及 next,由外向内依次执行。当最里面的 next 执行完成之后,next 后面的代码会由内向外执行。非常类似于 Koa 的洋葱中间件模型。
dispatch 之后,Redux 是如何去处理的?
- 执行
reducer更新currentState - 依次执行
listener函数
state 中的数据被修改之后,订阅者们如何去收到更新后的数据?
在 subscribe 中通过 store.getState() 获取数据
applyMiddleware 中 dispatch 为何赋值两次
第一次赋值表示 => 在 Middleware 创建时,不能进行 dispatch
第二次赋值表示 => 此 dispatch 是一个强化后的 dispatch
middlewareAPI 中的 dispatch 什么时候会被调用
注意使用场景
function logger({ getState, dispatch }) {
// 此时的 dispatch 为抛错函数,即第一次赋值的函数,即第 7 - 12 行
return next => {
// 此处的 next === store.dispatch
// 此时的 dispatch === 下面 return 的函数
return action => {
console.log('will dispatch', action)
const returnValue = next(action)
console.log('state after dispatch', getState())
return returnValue
}
}
}
middlewareAPI 中的 dispatch 为啥要用匿名函数包裹
let dispatch = () => console.log('Error!');
const obj = {
name: 'dispatch',
dispatch,
}
obj.dispatch();
dispatch = () => console.log('Ready!');
obj.dispatch();
上述的 log 是什么结果呢?
result
let dispatch = () => console.log('Error!');
const obj = {
name: 'dispatch',
dispatch: (...args) => dispatch(...args),
}
obj.dispatch();
dispatch = () => console.log('Ready!');
obj.dispatch();
上述的 log 是什么结果呢?
result
store.subscribe 为啥要有🔐 isDispatching
dispatch 执行时候会循环执行更新函数,要保证 listeners 数组在这时候不能被改变
以下代码作用
createStore
答:有了这一层判断,我们就可以这样传:createStore(reducer, initialState, enhancer) 或者这样:createStore(reducer, enhancer),其中 enhancer 还会是 enhancer。
Redux 优势
- 纯函数,做测试的时候 easy









网友评论