源码:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 注入了store
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 注入了每个中间件的next
dispatch = compose(...chain)(store.dispatch)
// 现在的dispatch就是接收一个action,会依次沿着中间件链依次调用的函数。
return {
...store,
dispatch
}
}
}
主要步骤:
1.中间件串联:
目的:将所有的middleware串联在一起,并保证最后一个执行的是dispatch(action)。
compose方法:从左到右组合多个函数。
compose(funcC, funcB, funcA)() === funcC(funcB(funcA()))
function compose(...funcs) {
if(funcs.length === 0) {
return args => args
}
if(funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
思路:用compose方法组合函数,封装最后一个函数作为dispatch(action)方法。
const middleware1 = action => action;
const middleware2 = action => action;
const final = action => store.dispatch(action);
compose(final, middlewares2, middleware1)(action);
2.中间件可访问store的state
给每个middleware都传递store,保证中间件访问到的store是一致的。
const middleware1 = (store, action) => action;
const middleware2 = (store, action) => action;
const final = (store, action) => store.dispatch(action);
但是现在就没有办法使用compose函数进行组合了,因为参数类型要求是(store, action),而上一个中间件返回的是action,所以要使用函数柯里化配合compose:
通过循环将store传递给所有的中间件,这里是延迟计算的思想。
const middleware1 = (store) => action => action;
const middleware2 = (store) => action => action;
const final = (store) => action => dispatch(action);
const chain = [final, middleware2, middleware1].map(midItem => midItem(store));
compose(...chain)(action);
3.中间件调用的dispatch方法
在源码中可以看到,store的dispatch是被改装过的dispatch(含有中间件调用链),所以如果我们在中间件在再使用这个dispatch,会造成死循环。所以还得给每个中间件传入原生的dispatch。
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 现在dispatch被改变了,但是中间件用的还是原生dispatch方法
dispatch = compose(...chain)(store.dispatch);
4.保证中间件不断裂
我们之前定义了中间件的格式是 mid = store => action => action,但要怎么保证中间件不会因为没有返回action而断裂呢?答案是:要保证上一个中间件有下一个中间件的注册,就不会断裂。所以next就是执行下一个中间件的方法,最后一个next是dispatch(action).
const middleware1 = store => next => action => {console.log(1); next(action);}
const middleware2 = store => next => action => {console.log(2); next(action);}
const chain = [middleware1, middleware2].map(midItem => midItem({
dispatch: (action) => store.dispatch(action);
}))
dispatch = compose(...chain)(store.dispatch);
总结:
(store) => (next) => (action) => {...next(action);...}
- 中间件机制的核心是使用compose组合函数,将所有的中间件串联起来。
- 配合compose对单参数的使用,对每个中间件使用柯里化的设计,使每个中间件共享store的state和dispatch。
- 为了保证中间件不会因为没有返action而断裂,用next来保证可以本个中间件中注册下一个中间件。next最后执行的是store.dispatch。
附:redux-thunk的实现
function createThunkMiddleware(extraArguments) {
return ({ dispatch, getState}) => (next) => (action) => {
if(typeof action === 'function') {
return action(dispatch, getState, extraArguments);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddlewawre;
exprt default thunk;