Redux真正强大之处便是中间件系统,利用各类自定义或第三方的中间件,可以极大丰富其功能,派发的action也将不再局限于普通纯对象。
还记得createStore里enhancer参数吗?在createStore.js的53行,如果传递了enhancer,则:
return enhancer(createStore)(reducer, preloadedState)
接着看下边的实例:
const initialState = {
todos:[]
};
const reducer = function(state,action){
switch (action.type){
case "TODO_ADD":
return Object.assign({},state,{
todos:state.todos.concat({text:action.text})
})
}
return state;
};
const enhancer = function(createStore){
return function(reducer, preloadedState){
return createStore(reducer,preloadedState);
}
};
const store = createStore(reducer,initialState,applyMiddleware(enhancer));
可以看出来,这里store是执行enhancer的返回函数所创建的。其实enhancer就是applyMiddleware的返回结果,我们来看看applyMiddleware的源码:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
这个看起来不够直观,转成ES5,那么就是这样的:
function applyMiddleware(...middlewares){
return function(createStore){
return function(reducer,preloadedState){
const store = createStore(reducer,preloadedState);
let dispatch = function(){
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
};
const middlewareAPI = {
getState: store.getState,
dispatch:function(...args){
return dispatch(...args);
}
};
const chain = middlewares.map(function(middleware){
return middleware(middlewareAPI);
});
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
对比我们的enhancer函数,是不是很像呢,接着逐行分析最里面的代码。
const store = createStore(reducer,preloadedState);
let dispatch = function(){
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
};
创建store,自定义dispatch错误函数,这个函数的意思:不能在中间件里面直接调用根dispatch,应该调用形参dispatch。
const middlewareAPI = {
getState: store.getState,
dispatch:function(...args){
return dispatch(...args);
}
};
const chain = middlewares.map(function(middleware){
return middleware(middlewareAPI);
});
定义middlewareAPI对象,包含getState和dispatch俩个属性。
定义chain数组,每个元素是middleware的执行结果(依然是函数),参数就是middlewareAPI。在中间件里面不能直接调用middlewareAPI的dispatch。
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
结合上一章compose的解读,那么中间件的执行顺序就是由后向前的,执行队列的每个dispatch形参都由上个中间件的执行结果返回。
结合上边的分析,那么一个最最简单的中间件应该是这种格式的。
function middleware(middlewareAPI){
return function(next){
return function(action){
next(action);
}
}
}
我们定义俩个中间件:
function middleware1(middlewareAPI){
return function ware1(next){
return function(action){
console.log("middleware1开始");
next(action);
console.log("middleware1结束");
}
}
}
function middleware2(middlewareAPI){
return function ware2(next){
return function(action){
console.log("middleware2开始");
next(action);
console.log("middleware2结束");
}
}
}
结合上边的源码,那么在applyMiddlewares中数组常量chain的格式就是这样的:
const chain = [
function ware1(next){
return function(action){
console.log("middleware1开始");
next(action);
console.log("middleware1结束")
}
},
function ware2(dispatch){
return function(next){
console.log("middleware2开始");
next(action);
console.log("middleware2结束")
}
},
];
接着经过compose函数的运算,所以原始的dispatch会作为ware2中间件的参数,然后ware2的返回值会作为ware1的参数,那么最终dispatch函数就是这样子的:
const dispatch = function(action){
console.log("middleware1开始");
void function(next){
console.log("middleware2结束");
next(action);
console.log("middleware2结束")
}(next)
console.log("middleware1结束")
};
那么当派发一个action之后,正确的执行顺序就是:
综上,调用next之前可以获取到当前state树,调用结束后可以获取到更新后的state树,基本中间件格式:
function middleware(middlewareAPI){
return function(next){
return function(action){
console.log("middleware开始",middlewareAPI.getState());
next(action);
console.log("middleware结束",middlewareAPI.getState());
}
}
}
同时中间件的引用顺序也是有讲究的,第一个中间的action回传给redux必须是纯对象。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。