中间件的思想和koa的洋葱模型类似,当遇到next时交给下一个中间件处理,当最后一个中间件处理完毕后,依次退回到上一个next之后执行 主要通过reduce将之后的函数作为参数传递给上一个函数,则上一个函数调用参数就会执行下一个函数,在下一个函数执行完后退回
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
————————————————
- 若传递的是compose(e,f,g) ,则第一次调用返回(…args) => e(f(…args))
- 第二次调用,a为(…args) => e(f(…args)),b为g
- 则最终调用结果为(…args) => e(f(g(…args)))
一个中间件格式示例:
function M1(store) {
return function(next) {
return function(action) {
console.log('A middleware1 开始');
next(action)
console.log('B middleware1 结束');
};
};
}
applyMiddleware
let store = applyMiddleware(loggerMiddleware)(createStore)(rootReducer);
export default function applyMiddleware(...middlewares) {
return (next) =>
(reducer, initialState) => {
var store = next(reducer, initialState);
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
};
};
}
- chain的作用将store的getState方法、和改造的dispatch传给中间件,并返回第二层函数,chain = [(next)=>(action)=>{…}, (next)=>(action)=>{…}, (next)=>(action)=>{…}]
- 传递改造后的disptach原因看下面的redux-thunk
- compose的作用:compose(A, B, C, arg) === A(B(C(arg)))
- 则最后一个中间件的next是store.dispatch这个操作store的原始方法
- A的next是B的action方法,B的next是C的action方法,C的next是store.dipatch
- 又因为A执行后为(action)=>{…},所以添加中间件改造后的dispatch传入的action,能够通过next传递给下一个中间件的(action)=>{…},最终通过最后一个中间件的next即store.dispatch传递给reducer
function M1(store) {
return function(next) {
return function(action) {
console.log('A middleware1 开始');
next(action)
console.log('B middleware1 结束');
};
};
}
function M2(store) {
return function(next) {
return function(action) {
console.log('C middleware2 开始');
next(action)
console.log('D middleware2 结束');
};
};
}
function M3(store) {
return function(next) {
return function(action) {
console.log('E middleware3 开始');
next(action)
console.log('F middleware3 结束');
};
};
}
function reducer(state, action) {
if (action.type === 'MIDDLEWARE_TEST') {
console.log('======= G =======');
}
return {};
}
react-thunk 基本使用
export const increment=(num)=>({type:INCREMENT,data:num})
export const incrementAsync=(num)=>{return dispatch=>{
setTimeout(()=>{
dispatch(increment(num))
},1000)
}}
...
this.props.incrementAsync(num);
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
- 因为这里使用了react-redux高阶函数this.props.incrementAsync(num)的调用,相当于改造的dispatch(incrementAsync(num))
- dispatch即为createThunkMiddleware中的action函数,当调用时,因为参数action为一个函数,即
dispatch=>{
setTimeout(()=>{
dispatch(increment(num))
},1000)
}
- 所以会走action(dispatch, getState, extraArgument)
- 将改造后的dispatch传递进去,又因为在这里改造后的dispatch就是createThunkMiddleware中的action函数,所以调用时又回去了,不过此时if判断中action不再是一个函数,而是一个{type:…}对象,所以此时会走next(action)交给下一个中间件处理
- 这也是为什么传递给中间件的dispatch是改造后的dispatch的原因,因为在redux-thunk中会调用一次重新走中间件的流程
- redux-thunk的异步处理其实就是在异步任务中调用dispatch,和同步任务没啥区别,纯属脱了裤子放屁。。。
redux-logger
- redux-logger建议放在中间件的最后一位是因为最后一位能保证next为store.dispatch而非改造后的dispatch,所以使用store.dispatch更新store能准确快速拿到更新后的state
export default function createLogger({ getState }) {
return (next) =>
(action) => {
const console = window.console;
const prevState = getState();
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`;
console.log(`%c prev state`, `color: #9E9E9E`, prevState);
console.log(`%c action`, `color: #03A9F4`, action);
console.log(`%c next state`, `color: #4CAF50`, nextState);
return returnValue;
};
}
|