中间件之前我在写koa还有express的时候用过不少,主要是将逻辑分开,然后去一层一层的去判断执行是否符合要求比如权限之类的,就相当于一个洋葱一样,一层一层的往里深入。 除了koa还有express之外,redux的中间件也感觉比较牛,redux可以说是百行代码千行文档。在redux中我们常用的中间件有日志记录(logger),还有处理异步请求(thunk) 这次博客主要用来记录一下我在看redux中间件源码过程中的体会,还有他中间件源码的执行流程,理解大佬们的思维
起始
我们在创建store会调用createStore方法,并且我们还会传入中间件如下
typescript
const store = createStore(reducer,initState,applyMiddleware(thunk))
这个时候我们可以看到,他主要是两个方法,一个是createStore,一个是applyMiddleware,我们就从createStore入手
typescript
function createStore<
S,
A extends Action,
Ext extends {} = {},
StateExt extends {} = {},
PreloadedState = S
>(
reducer: Reducer<S, A, PreloadedState>,
preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
// 省略边界判断代码(主要是用于判断参数的类型)
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState | undefined
)
}
我们可以看到它首先是将applyMiddleware函数的执行结果(enhancer)执行并传入createStore(用于初始化和获取state),并把返回enhancer的返回结果的执行结果,执行时传入reducer和preloaderState(我们传入的initState)
applyMiddleware
看完createStore,我们并不能得到中间件的原理,所以我们看一下applyMiddleware
typescript
function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any> {
return createStore => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)
let dispatch: Dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
首先该方法,直接返回一个函数,该函数接受createStore,然后又直接返回一个函数,接受reducer和preloadedState,这个函数中初始化store,并且定义了dispatch,然后把所有的中间件都执行一遍,传入middlewareAPI,得到一个chain,然后dispatch等于compose的返回值
compose
我们看完applyMiddleware并不能得到中间件的具体处理,不过我们可以看到,中间件的主要原理就是劫持dispacth,这样我们修改数据提交dispatch时,它背后并没有直接就触发修改数据的dispatch,而是触发compose处理过的dispatch,那么我们就来看一下compost代码
typescript
function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce(
(a, b) =>
(...args: any) =>
a(b(...args))
)
}
可以看出来,就是返回一个函数,如果是多个中间件这个函数是层层嵌套的函数,然后接受一些参数(这里的参数主要是真实的dispatch)这里需要注意的一点是,只有最后一个中间件才能拿到真实的dispatch,而其他中间件拿到的是下一个中间件,就这样一层层传递,最后到达真实的dispatch 为了方便理解,我这里模仿了一下redux的执行流程
typescript
function a(next) {
return (action) => {
console.log('a-----------');
console.log(next, 'next');
console.log(action, 'action');
console.log('-----------a');
next(action)
}
}
function b(next) {
return (action) => {
console.log('b-----------');
console.log(next, 'next');
console.log(action, 'action');
console.log('-----------b');
next(action)
}
}
function c(next) {
return (action) => {
console.log('c-----------');
console.log(next, 'next');
console.log(action, 'action');
console.log('-----------c');
next(action)
}
}
const chain = [a, b, c]
const compose = (...funs) => {
return funs.reduce(
(a, b) =>
(...args) =>
a(b(...args))
)
}
const dispatch = () => {
console.log('我是原始的dispatch');
}
const m = compose(...chain)(dispatch)
m({ state: '触发' })
上边的m就是我们平常触发的action,然后它会一层一层的传递,从a执行到b然后再从b执行到c,最后c触发真实的action,这里的a,b,c就相当于中间件(不过仍需要注意一点就是,真实的中间件是三层函数,因为我们这里并没有const chain = middlewares.map(middleware => middleware(middlewareAPI))这个操作,所以就直接返回了)传递的参数next是中间件返回的函数 由此我们就知道了中间件的原理,不得不说,大佬写的代码还是挺牛的
redux-thunk中间件原理
这个中间件主要是为了异步操作的,虽然我们可以再触发action时异步触发,但是这个逻辑并不能复用,由此就有了该中间件,其实我感觉这个中间件并非是支持异步,而是支持action为函数,我们平常写的action都是一个对象的形式,有了该中间件,我们可以让action是一个对象 看了上边的原理之后,我们可以发现中间件写的思路其实挺固定的,就是写一个函数,里边返回了三层函数,去调传递的next参数,然后一步一步的像一个洋葱一样最终触发到真正的修改数据的action 其实thunk的源代码非常短我们,主要还是感受大佬的思维
typescript
const thunk=()=>{
const middleware: ThunkMiddleware<State, BasicAction, ExtraThunkArg> =
({ dispatch, getState }) =>
next =>
action => {
if (typeof action === 'function') {
// 执行后,在action函数中触发dispatch
return action(dispatch, getState, extraArgument)
}
// 走其他中间件
return next(action)
}
return middleware
}
简单来说就是判断action传递的是否是函数,然后去走不同的逻辑