为了解析中间件,先看一下几个中间件是什么样子,怎么用,运行起来的原理是什么?
1、中间件是什么样子的
1.2 thunk中间件
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 如果是函数,就执行函数
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 如果不是,执行下一个中间件
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
1.2promise中间件
import isPromise from 'is-promise';
import { isFSA } from 'flux-standard-action';
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action) ? action.then(dispatch) : next(action);
}
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}
1.3logger中间件
const defaultLogger = ({ dispatch, getState } = {}) => {
if (typeof dispatch === 'function' || typeof getState === 'function') {
return createLogger()({ dispatch, getState });
}
};
function createLogger(options = {}) {
const loggerOptions = Object.assign({}, defaults, options);
const {
logger,
stateTransformer,
errorTransformer,
predicate,
logErrors,
diffPredicate,
} = loggerOptions;
if (typeof logger === 'undefined') {
return () => next => action => next(action);
}
import { logger } from 'redux-logger'
const store = createStore(
reducer,
applyMiddleware(logger))
import { createLogger } from 'redux-logger'
const logger = createLogger({
// ...options
});
const store = createStore(
reducer,
applyMiddleware(logger));
return () => next => action => next(action);
}
const logBuffer = [];
return ({ getState }) => next => (action) => {
if (typeof predicate === 'function' && !predicate(getState, action)) {
return next(action);
}
const logEntry = {};
logBuffer.push(logEntry);
logEntry.started = timer.now();
logEntry.startedTime = new Date();
logEntry.prevState = stateTransformer(getState());
logEntry.action = action;
let returnedValue;
if (logErrors) {
try {
returnedValue = next(action);
} catch (e) {
logEntry.error = errorTransformer(e);
}
} else {
returnedValue = next(action);
}
logEntry.took = timer.now() - logEntry.started;
logEntry.nextState = stateTransformer(getState());
const diff = loggerOptions.diff && typeof diffPredicate === 'function'
? diffPredicate(getState, action)
: loggerOptions.diff;
printBuffer(logBuffer, Object.assign({}, loggerOptions, { diff }));
logBuffer.length = 0;
if (logEntry.error) throw logEntry.error;
return returnedValue;
};
}
export { defaults, createLogger, defaultLogger as logger };
export default defaultLogger;
2、怎么使用中间件
const store = createStore(rootReducer, initialState,
applyMiddleware(thunk),
... ...
);
简单来说,createStore做了这么件事:
目的:根据你传入的reducer和初始状态initialState生成初始化store,并提供了一些列操作的接口,像dispatch等
怎么做的呢?参考Redux-creatStore/compose
本文重点讲解中间件的执行过程和原理
3、中间件运行原理
中间件的执行原理和koa中间件的执行原理类似,但是不是洋葱型的,而是半个洋葱,因为redux是单向执行的,走过去就完事了。
当你应用了中间件,在触发一个action操作的时候,action操作就会经过先经过中间件,最终再形成dispatch(action)。
以其中两个中间件为例,说明下,一个触发一个action动作的时候,代码的执行逻辑。
thunk:是允许dispatch一个函数,而不是一个对象
假如说异步打印一个日志。
3.1 中间件的内部逻辑
const store = createStore(reducer, preloadedState, enchancer);
// 如果没有中间件,正常触发一个action;
// 如果有中间件的时候 creatStore内部的执行逻辑是这样的
// enchancer 就是你应用的中间件,调用applyMiddleware得到的组合中间件
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 该创建的store还是要创建的,只传入了两个参数,没有中间件,得到的是正常的store
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.`
)
}
// 把getState、dispatch传给中间件
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 下面方法返回来了一个函数数组,中间件被剥离到
// next => {}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 再执行下面的,中间件就被剥离到
// action => {}
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
// 下面得到的结果是
// 假设中间件为 a b
// a(b(store.dispatch))
return enchancer(createStore)(reducer, preloadedState);
// 结合上面thunk的源码
({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
};
经过上面的操作后,经过中间件包装后的store是什么样子
假设中间件为 a b c
没有中间件的时候是这样的
store = {
dispatch,
... ...,
subscribe,
getState
}
经过中间包装后,store变成了
store = {
dispatch: a(b((store.dispatch))),
... ...,
subscribe,
getState
}
总结起来就是给每一个中间件配发了一个原始store的dispatch,中间件函数嵌套执行
3.2 触发一个action时,执行逻辑
假设触发一个异步打印日志的功能
- 应用中间件
const store = createStore(rootReducer, initialState,
applyMiddleware(thunk)
);
经过上面的操作,现在的store应该是
{
dispatch: action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
},
... ...,
subscribe,
getState
}
- action函数
当执行这个logNext的时候,返回一个函数,函数的参数是dispatch和getState两个。
const logNext = () => (dispatch, getState) => {
setTimeout(
dispatch({
type: 'LOG',
payload: {
content: '这是一条异步日志'
}})
,5000);
}
- 执行过程
---->
store.dispatch(logNext()) // 传了一个函数,然后执行一个函数
---->
(dispatch, getState) => {
setTimeout(
dispatch({
type: 'LOG',
payload: {
content: '这是一条异步日志'
}})
,5000);
}
---->
可以看出来,redux-thunk就是一个封装函数,允许store.dispatch一个函数
如果有多个中间件,执行过程是什么样子的?重点在next(action),next是什么呢?
next就是每一个中间件要做的事情
next => action => {}
明白了么?
附录
compsoe
// compose本身并不改变函数的执行,将函数组合后又返回了一个函数
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)))
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。