由于一直用业界封装好的如redux-logger、redux-thunk此类的中间件,并没有深入去了解过redux中间件的实现方式。正好前些时间有个需求需要对action执行时做一些封装,于是借此了解了下Redux Middleware的原理。* 中间件概念首先简单提下什么是中间件,该部分与下文关系不大,可以跳过。来看眼这个经典的图。不难发现:不使用middleware时,在dispatch(action)时会执行rootReducer,并根据action的type更新返回相应的state。而在使用middleware时,简言之,middleware会将我们当前的action做相应的处理,随后再交付rootReducer执行。简单实现原理比如现有一个action如下:function getData() { return { api: ‘/cgi/getData’, type: [GET_DATA, GET_DATA_SUCCESS, GET_DATA_FAIL] }}我们希望执行该action时可以发起相应请求,并且根据请求结果由定义的type匹配到相应的reducer,那么可以自定义中间件处理该action,因此该方法封装成中间件之前可能是这样的:function dispatchPre(action, dispatch) { const api = action.api; const [ fetching_type, success_type, fail_type] = action.type; // 拉取数据 const res = await request(api); // 拉取时状态 dispatch({type: fetching_type}); // 成功时状态 if (res.success) { dispatch({type: success_type, data: res.data}); console.log(‘GET_SUCCESS’); } // 失败时状态 if (res.fail) { dispatch({type: fail_type}); console.log(‘GET_FAIL’); };}// 调用: dispatchPre(action())那如何封装成中间件,让我们在可以直接在dispatch(action)时就做到这样呢?可能会首先想到改变dispatch指向// 储存原来的dispatchconst dispatch = store.dispatch;// 改变dispatch指向store.dispatch = dispatchPre;// 重命名const next = dispatch;截止到这我们已经了解了中间件的基本原理了~源码分析了解了基本原理能有助于我们更快地读懂middleware的源码。一般我们会这样添加中间件并使用。createStore(rootReducer, applyMiddleware.apply(null, […middlewares]))接下来我们可以重点关注这两个函数createStore、applyMiddlewareCreateStore// 摘至createStoreexport function createStore(reducer, rootState, enhance) { … if (typeof enhancer !== ‘undefined’) { if (typeof enhancer !== ‘function’) { throw new Error(‘Expected the enhancer to be a function.’) } /* 若使用中间件,这里 enhancer 即为 applyMiddleware() 若有enhance,直接返回一个增强的store对象 / return enhancer(createStore)(reducer, preloadedState) } …}ApplyMiddleware再看看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 } }}执行步骤可以将其主要功能按步骤划分如下:1、依次执行middleware。将middleware执行后返回的函数合并到一个chain数组,这里我们有必要看看标准middleware的定义格式,如下export default store => next => action => {}// 即function (store) { return function(next) { return function (action) { return {} } }}那么此时合并的chain结构如下[ …, function(next) { return function (action) { return {} } }]2、改变dispatch指向。想必你也注意到了compose函数,compose函数如下:[…chain].reduce((a, b) => (…args) => a(b(…args)))实际就是一个柯里化函数,即将所有的middleware合并成一个middleware,并在最后一个middleware中传入当前的dispatch。这里再使用一个简单的例子方便大家理解。// 假设chain如下:chain = [ a: next => action => { console.log(‘第1层中间件’) return next(action) } b: next => action => { console.log(‘第2层中间件’) return next(action) } c: next => action => { console.log(‘根dispatch’) return next(action) }]调用compose(…chain)(store.dispatch)后返回a(b(c(dispatch)))。可以发现已经将所有middleware串联起来了,并同时修改了dispatch的指向。最后看一下这时候compose执行返回,如下dispatch = a(b(c(dispatch)))// 调用dispatch(action)// 执行循序/ 1. 调用 a(b(c(dispatch)))(action) print: 第1层中间件 2. 返回 a: next(action) 即b(c(dispatch))(action) 3. 调用 b(c(dispatch))(action) print: 第2层中间件 4. 返回 b: next(action) 即c(dispatch)(action) 5. 调用 c(dispatch)(action) print: 根dispatch 6. 返回 c: next(action) 即dispatch(action) 7. 调用 dispatch(action)*/