要想实现一个 redux 中间件咱们必须了解 redux 的基本实现原理。本文将从 redux 源码入手,重点讲解 applyMiddleware 如何将中间件串联执行。只有理解了底层原理咱们才能够游刃有余的写出一个 redux 中间件。javascript
redux 经过 createStore 来建立一个 store 对象html
要理解 applyMiddleware 的实现原理,咱们要从 createStore 入手java
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
// 篇幅有限,后面被我省略了,有兴趣请去看 redux 源码
// ......
复制代码
能够看见 createStore 的三个参数依次为: reducer, preloadedState, enhancer。参见源码,若是传入了 enhance 参数且为函数,则将 createStore 传入 enhancereact
return enhancer(createStore)(reducer, preloadedState)
ios
也就是说,如今咱们将用 enhance 来建立一个 store 对象。json
通常状况下 createStore 的第三个参数 enhance 就是 applyMiddlewaveredux
applyMiddlewave 的代码只有二十多行倒是本文的重点axios
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
参见 createStore 的源码能够得知:applyMiddlewave 依然使用 createStore 建立了store 对象而且返回,只是改写了这个对象的 dispatch 方法。数组
下面咱们重点来看这个被改写掉的 dispatch 方法,同时理解它和原生 dispatch 方法的区别也是本文的重点。为了更直观的了解这个过程咱们先来看一个 简单的中间件实现 logger middlewave闭包
export default store => next => action => {
const start = Date.now();
next(action);
const ms = Date.now() - start;
console.log(`dispatch: ${action.type} - ${ms}ms`);
}
复制代码
下面分二步详细探讨中间件的运行原理
将原生的 getState 和 dispacth 做为第一个参数传入中间件数组,得到执行完的 chain 数组;
chain = middlewares.map(middleware => middleware(middlewareAPI))
组合串联 middlewave
dispatch = compose(...chain)(store.dispatch)
compose 将全部的中间件串联起来组成新的 dispatch
compose 源码
function compose(...funcs) {
return arg => funcs.reduceRight((composed, f) => f(composed), arg);
}
复制代码
参考咱们的 logger middlewave 这里的 composed 便是咱们的 next 参数。
reduceRight 和 ruduce 同样,不过 reduceRight 是从数组的右端开始执行,arg 将做为 reduceRight 的初始值(这里就是 store.dispatch)。假设咱们的 chain 数组为 [f1,f2,f3]
执行完毕后 dispatch 为 dispatch = f1(f2(f3(store.dispatch))))
,调用这个新的 dispatch 每一个中间件就能依次执行了,这里的中间件执行过程也是相似于 Koa 的中间件是很是经典的洋葱模型。只有最后一个中间件会触发 redux 原生的 dispatch,将这个 action 分发出去。(没错,我就是灵魂画师)
通常而言 dispatch 只能分发一个 action 对象,可是使用了 redux-thunk 中间件咱们却能够分发一个异步函数。
const thunk = store => next => action => {
typeof action === 'function' ?
action(store.dispatch,store.getState) :
next(action)
}
复制代码
一个异步的 action 的示例
function getMessage = (dispatch, getState) => {
axios.get('xxx/xxx')
.then((res) => {
dispatch({
type: 'GET_MESSAGE_SUCCESS',
message: res.json(),
});
})
.catch((err) => {
dispatch({
type: 'GET_MESSAGE_ERROR',
message: 'error'
});
});
}
复制代码
这里的 dispatch 任然是改造后的 dispatch 由于传入中间件的第一个参数 store 即 middlewareApi 中的 dispatch 是一个闭包保存着对最外层函数 dispatch 的引用,因此当 diapatch 被改写后后面调用的 dispatch 都是这个新的 dispatch(即中间件的串联),因此即便在异步 action 中分发一个 action 依然会将所有中间件再执行一遍。
因此理解了以上,编写一个中间件将超级简单,只须要按照中间件编写规范
function myMiddleware = store => next => action = {
// 在这里你能够拿到 store.getState 和 store.dispatch
// 注意若是你调用 store.dispatch 中间件又重新从最外层开始 若是不加限制条件将陷入死循环
// do something
next(action) // 进入下一个中间件,最后一个中间件的 next 参数为 redux 原生 dispatch
// 返回继续执行这个中间件剩余部分
}
复制代码
深刻理解 redux 中间件的实现原理,可让咱们在平常工做中,对 redux 数据流向更加清晰和对本身的程序更加有把握。本人水平有限,若有错误还请指出。
原创文章,转载请注明原地址
若是你喜欢的话,可不能够给我点个当心心(*^__^*) 嘻嘻……