注:本文不会仔细分析redux-react
middleware究竟是什么?
first look code!
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
return dispatch
compose的写法很美妙(FIFO遍历):
compose = middlewares.reduce((a,b) => (...args) => b(a(...args)))
redux的实现是FILO
compose = middlewares.reduce((a,b) => (...args) => a(b(...args)))
// FIFO和FILO的实现 取决于最内层函数是第一个中间件还是最后一个中间件
// 这里实现非常巧妙,递归倒换执行顺序,省去了reverse,可以学习一下
再次强调,compose的写法很美妙,我们详细分析一下
- 每次reduce里b(...args)其实就是middleware的
next,store.dispatch是最后一个中间件处理完成后的调用的真实dispatch,此时action已经纯化 - 以FILO的形式遍历栈,为每个中间件传入next
- 重点:返回第一个middleware的最终函数,即
action => {}
初次看compose会觉得很复杂,写一个容易理解的版本
dispatch = (...middlewares) => {
middlewares.reverse()
let next = store.disptach
middlewares.forEach((middleware,i) => {
middlewares[i] = middleware(next)
next = middlewares[i]
})
return _.last(middlewares)
}
到这里,理一下dispatch的逻辑:
- 遍历中间件,为每一个中间件curry一个middlewareAPI进去
- 遍历中间件,为每一个中间件传入next,并返回第一个中间件
- 此时dispatch就是串联好的中间件组的头部
所以middleware是action -> reducers 这个过程中对数据的处理函数
想一想thunk,saga是不是这样
附:
export interface Dispatch<A extends Action = AnyAction> {
<T extends A>(action: T, ...extraArgs: any[]): T
}
export interface AnyAction(extends Action){
type:T;
[extraProps: string]: any;
}
createStore做了什么?
createStore定义
export default function createStore(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
){
store有几个重要的方法
- subscribe (redux-react就是在compose函数里为每个组件subscribe了patch,再由patch触发的组件更新)
- dispatch
subscribe不能在dispatch时执行,所以两个一起讲
#####dispatch一些核心代码
//...
try {
isDispatching = true
currentState = currentReducer(currentState, action) // 这就是为什么reducer不能是异步
} finally {
isDispatching = false
}
// try finally 只是为了清除isDispatching表示,错误依然由调用者处理
//...
// 对应下面subscribe的ensureCanMutateNextListeners
const listeners = (currentListeners = nextListeners)
// 触发订阅者响应,redux-react里rerender订阅组件
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
sub/unsubscribe一些核心代码
if (isDispatching) throw new Error(...) // 首先不能在dispatch里subscribe,避免影响订阅队列
let isSubscribed = true
ensureCanMutateNextListeners() // 如果在下次dispatch之前再次新增订阅,不复制next缓冲
nextListeners.push(listener)
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 重点提一下ensureCanMutateNextListeners函数
/*store维护了nextListener和currentListener两个类,以保证在dispatch时执行的是一个snapshot,* 而非nextListener(最新值)。这只是为了处理某种极端情况
* 该情况如下,某个listener里进行了unsubscribe,此时listeners数组会产生变化,for循环遍历将会出现问题
*/
事实上,subscribe运用了双缓冲技术,防止该次处理时订阅队列发生变化。这种技术在V8 GC 里cheney算法,以及fiber架构上都有广泛运用
Tools函数
bindActionCreators
Action Creator 很简单,就是一个创建 action 的函数。不要混淆 action 和 action creator 这两个概念。Action 是一个信息的负载,而 action creator 是一个创建 action 的工厂。
react应用里,bindActionCreators的作用是让用户无redux感知的调用dispatch
总结
其实redux就是两个很简单的逻辑
reducer(middleware(dispatch(action)))- emit subscribe
虽然细节上有值得我们学习的地方,但整体上只是flux的简单实现,真正的精髓在于结合react该如何应用。
redux已经成了一个闭环,在react应用里,redux-store就是一个黑盒,如何做到利用这个非响应式的仓库完成尽量精确的状态依赖,才是一系列redux-react的核心。那也才是真正值得学习的源码,以后有机会会讲讲react-redux库和mobx-react-lite的源码。
觉得还行点个赞吧。。。