这是第二次读 Redux 源码了,上一次还是 16 年后半年,整个流程下来要比第一次在收获和理解上有了长足的进步。废话不多说,进入正题 …

createStore

包含订阅函数、清除订阅、dispatch 函数。
在 window 全局上定义了三个变量:

  1. globalReducer(用来承接单个或者合并后的 reducer)
  2. globalState 用来承接产生的新的 store 集合
  3. subscriberList 用来存放所有的订阅者回调函数

最终的 store 返回的内容是这样的:

{
subscribe,
clearSubscribe,
dispatch,
getState
}

dispatch 函数
调用 reducer 的 API(其实是 combineReducers 接口,它将多个 reducers 合并为一个 reducers),将 state 和 action 传入,更新 globalState,然后再依次调用每一个观察者函数。

combineReducers

  1. reducers 使用 object 的方式来输入,输出则是一个新的 reducer(也就是所谓的 globalReducer)
  2. 筛选出 function 类型的 reducer 作为新的 object
  3. nextState 维护一个 object,{reducerName: reducerState}, 这里的 reducer 其实对应着每一个的子 state,所以有必要给每一个 reducer 都分别维护一份自己的 store
  4. 执行的时候每一个 reducer 的 state 也都是之前 preState[key] 的内容

applyMiddleware

applyMiddleware 是怎么实现对 createStore 和 reducer 改造的呢?

API

  1. middleware 本身的 API:
    其实是一个函数:
    store => next => action => {}
    (这里前两层函数在创建链的过程中已经被执行,最终返回的是 dispatch 链)
  2. applyMiddleware(middlewares)(createStore)(reducers), 最终返回的是具备一系列中间件功能的 store, 包含 {…store, dispatch}

applyMiddleware 实现细节

function compose(...functionList) {
if (functionList.length === 0) {
return arg => arg;
}
if (functionList.length === 1) {
return functionList[0];
}
//箭头函数要注意return, reduceRight是为了让中间件按从左到右的方式执行
//一层层封装dispatch, 链式调用
return (...args) => functionList.reduceRight((composed, f) => f(composed), ...args);
}


export default function applyMiddleware(...middlewares) {
return createStore => (reducer, initState) => {
const store = createStore(reducer, initState);
let dispatch = store.dispatch;
const middlewareAPI = {
getState: store.getState,
//为了让最后各个middleware拿到的dispatch是最新的,
//这里必须用匿名函数 action => dispatch(action), 而不能直接用store.dispatch
dispatch: action => dispatch(action)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);//包装dispatch
return {
...store,
dispatch
};
};
}
  1. 先生成最基础的 store
  2. 取出 dispatch 函数
  3. 创建 middleware 的第一层参数 middlewareAPI = {getState, dispatch} // 这里需要注意的是 dispatch 的值为 action => dispatch(action)
  4. 为每一个 middleware 执行第一层,那么应该就是 next => action => {} 这样的一个数组
  5. 对 next => action => {} 进行 compose 组合,让若干个函数能够链式调用,然后再传入 store.dispatch 做初始函数,这时候 next => {} 已经被执行,最终就剩一连串的 action => {}。
    • 分析 compose 函数,使用 store.dispatch 作为初始函数,每一层返回的是 action => {} 函数,恰好符合 dispatch 的函数签名
    • 具体执行则是从左向右依次执行,最终执行 store.dispatch
  6. 如何让每一个 middleware 拿到的 dispatch 都是最新的呢 ?
    action => dispatch(action)。这里其实利用了闭包对变量作用域的影响,每一个中间件在被执行了第一层后,next => action => {} 依然持有 middlewareAPI 的引用,在连成链以及在 applyMiddleware 执行完毕之后,每一个dispatch 依然持有 applyMiddleware 作用域中 middlewareAPI 的引用。这时候 其中的 dispatch: action => dispatch(action) 已经是 dispatch 链了。

  7. 关于返回值

    1. 由于是链式调用,如果严格 return next() 的话,整个 dispatch 链将返回原装 dispatch 返回的内容,即 action。否则从某个节点开始,那么它返回的则是当前中间件的返回值。
    2. 另一方面,在每一个中间件中一定要执行 next,否则执行链断开最终无法发出真实的 dispatch。

thunk

源码

store => next => action => {
if(typeof action === function) {
return action(store.dispatch, store.getState)
}
return next(action)
}

使用 thunk 实现异步

dispatch((dispatch, getState) => {
dispatch(开始了)
return Promise().then((res) => dispatch(接收到数据了))
// 因为 thunk 中的源码是 return action(), 所以中间件也会返回它的返回值,promise,因而 dispatch 会接收到 Promise。
})

利用 middleware 实现

这里与 dispatch 实现异步不同,我们在这里使用 next 不断得去执行。
参考:

export default store => next => action => {
const {promise, types, ...rest} = action
if (!promise) {
return next(action)
}

const [REQUEST, SUCCESS, FAILED] = types
next({...rest, type: REQUEST})

return promise.then(response => response.json())
.then(responseData => {
next({
...rest,
type: SUCCESS,
updateTime: responseData.data.last_update,
days: genData({
data: responseData.data.list,
ok: responseData.ok
})
})
})
.catch(error => next({
...rest,
type: FAILED,
error
}))
}

如果发现是普通 action,那么直接 next 传递到下一个中间件上即可,否则返回 Promise,这里和 thunk 的 return action 类似。

donation