redux 一个可预测的 js 状态容器(Predictable state container for JavaScript apps), 在很多 React 项目中都会默认使用 react-redux 作为全局状态管理工具。
让我们先来看下源码仓库暴露出来的方法。redux 库一共导出如下几个方法 createStore, combineReducers, bindActionCreators, applyMiddleware, compose, DO_NOT_USE__ActionTypes。
createStore
先看下 createStore。 createStore 接受三个参数:reducer, preloadedState, enhancer
1 | /** |
并最终返回一个 state 对象:
1 | ... |
subscribe 函数:接受一个 listener 参数,然后将其推入 listener 数组 **nextListeners.push(listener)**,
最终返回一个 unsubscribe 函数来解除订阅。
dispatch 函数:接受一个 action 参数,action 最好是可序列化的对象,并且必须有一个非 undefined 的 type 属性 。
dispatch 函数主要做两件事情:
- 执行 reducer,返回新的 state tree。
- 通知(执行)订阅的 listeners。
1 | try { |
replaceReducer 函数:接受一个新的 reducer 来替换老的 reducer, 主要用在 code splitting 和 hot reloading 的情况下
observable 函数: 这个函数可以认为是对接 observable/reactive 类库的接口,比如 RxJS。
关于 observable 可以看下这篇文章:RxJS: 如何从头开始创建 Observable
combineReducers
1 | /** |
最终返回的是结合后的 reducer :
1 | return function combination(state = {}, action) { |
从上面的代码可以看出每个 reducer 会接受对应 key 的 state subtree(并不是整个 state object)以及 action 对象作为入参,返回的结果最终组合成一个新的 state。
另外, 返回的 combination 函数的签名和单个 reducer 是一样的,这样就可以嵌套的使用 combineReducers, 比如:
1 | const combinedReducer = combineReducers({ |
bindActionCreators
1 | /** |
需要注意的几点:
For any unknown actions, you must return the current state. If the current state is undefined, you must return the initial state. The initial state may not be undefined. If you don't want to set a value for the reducer, you can use null instead of undefined
compose
compose 是一个工具函数,接受多个函数作为参数,返回一个组合后的函数。 compose(f, g, h) 会返回 (…args) => f(g(h(…args))).
applyMiddleware
applyMiddleware(...middlewares)
函数返回一个 store enhancer,可以作为 createStore 的第三个参数
1 | export default function applyMiddleware(...middlewares) { |
middleware 的函数签名为: ({ dispatch, getState }) => next => action => { return next(...) }
:
middleware 先接受 { dispatch, getState } 对象做为入参,然后再通过 compose 函数接受 dispatch 作为入参,将之前 compose 的执行顺序又反了过来,middleware 的执行顺序为从左往右,执行 next 函数其实是下一个 middleware。但最后一个 middleware 的 next 实际上是原始的 store.dispatch 函数。最终用 compose 的 dispatch 函数替换原先 createStore 返回的 dispatch。于是,新的 dispatch 就可以根据参数的类型来做处理,如果参数符合则进行逻辑处理,如果不符合则 return next(arguments)
来执行下一个 middleware 。
比如 redux-thunk 这个 middleware,源码如下:
1 | function createThunkMiddleware(extraArgument) { |
可以看到 middleware 的签名确实是这样的。
另外需要注意,middleware 内部的 dispatch 函数其实也是 compose 后的 dispatch,并不是原始的 store.dispatch, 所以执行 dispatch 时会再次进入每个 middleware。比如 thunk,dispatch 一个 fetch 函数,在 fetch 执行获得 response 后,可以再次 dispatch 另一个 fetch:
1 | const fetch1 = dispatch => |
至此,redux 4.0.0-beta2 版本的源码就解析完了,其中概念的东西占的较多,所以上还是需要理解作者的设计思路,这样也有利于理解源码的逻辑