这是第二次读 Redux 源码了,上一次还是 16 年后半年,整个流程下来要比第一次在收获和理解上有了长足的进步。废话不多说,进入正题 …
createStore
包含订阅函数、清除订阅、dispatch 函数。
在 window 全局上定义了三个变量:
- globalReducer(用来承接单个或者合并后的 reducer)
- globalState 用来承接产生的新的 store 集合
- subscriberList 用来存放所有的订阅者回调函数
最终的 store 返回的内容是这样的:
{ |
dispatch 函数
调用 reducer 的 API(其实是 combineReducers 接口,它将多个 reducers 合并为一个 reducers),将 state 和 action 传入,更新 globalState,然后再依次调用每一个观察者函数。
combineReducers
- reducers 使用 object 的方式来输入,输出则是一个新的 reducer(也就是所谓的 globalReducer)
- 筛选出 function 类型的 reducer 作为新的 object
- nextState 维护一个 object,{reducerName: reducerState}, 这里的 reducer 其实对应着每一个的子 state,所以有必要给每一个 reducer 都分别维护一份自己的 store
- 执行的时候每一个 reducer 的 state 也都是之前 preState[key] 的内容
applyMiddleware
applyMiddleware 是怎么实现对 createStore 和 reducer 改造的呢?
API:
- middleware 本身的 API:
其实是一个函数:
store => next => action => {}
(这里前两层函数在创建链的过程中已经被执行,最终返回的是 dispatch 链) - applyMiddleware(middlewares)(createStore)(reducers), 最终返回的是具备一系列中间件功能的 store, 包含 {…store, dispatch}
applyMiddleware 实现细节
function compose(...functionList) { |
- 先生成最基础的 store
- 取出 dispatch 函数
- 创建 middleware 的第一层参数 middlewareAPI = {getState, dispatch} // 这里需要注意的是 dispatch 的值为 action => dispatch(action)
- 为每一个 middleware 执行第一层,那么应该就是 next => action => {} 这样的一个数组
- 对 next => action => {} 进行 compose 组合,让若干个函数能够链式调用,然后再传入 store.dispatch 做初始函数,这时候 next => {} 已经被执行,最终就剩一连串的 action => {}。
- 分析 compose 函数,使用 store.dispatch 作为初始函数,每一层返回的是 action => {} 函数,恰好符合 dispatch 的函数签名
- 具体执行则是从左向右依次执行,最终执行 store.dispatch
如何让每一个 middleware 拿到的 dispatch 都是最新的呢 ?
action => dispatch(action)。这里其实利用了闭包对变量作用域的影响,每一个中间件在被执行了第一层后,next => action => {} 依然持有 middlewareAPI 的引用,在连成链以及在 applyMiddleware 执行完毕之后,每一个dispatch 依然持有 applyMiddleware 作用域中 middlewareAPI 的引用。这时候 其中的 dispatch: action => dispatch(action) 已经是 dispatch 链了。关于返回值
- 由于是链式调用,如果严格 return next() 的话,整个 dispatch 链将返回原装 dispatch 返回的内容,即 action。否则从某个节点开始,那么它返回的则是当前中间件的返回值。
- 另一方面,在每一个中间件中一定要执行 next,否则执行链断开最终无法发出真实的 dispatch。
thunk
源码
store => next => action => { |
使用 thunk 实现异步
dispatch((dispatch, getState) => { |
利用 middleware 实现
这里与 dispatch 实现异步不同,我们在这里使用 next 不断得去执行。
参考:
export default store => next => action => { |
如果发现是普通 action,那么直接 next 传递到下一个中间件上即可,否则返回 Promise,这里和 thunk 的 return action 类似。