关于react.js:剖析redux中间件applyMiddleware

30次阅读

共计 3399 个字符,预计需要花费 9 分钟才能阅读完成。

Middleware 是什么

Middleware 只是包装了 store 的 dispatch 办法。技术~~~~ 上讲,任何 middleware 能做的事件,都可能通过手动包装 dispatch 调用来实现,然而放在同一个中央对立治理会使整个我的项目的扩大变的容易得多。

从新包装 dispatch 办法

为什么要从新包装 dispatch

中间件的作用能够让咱们决定什么时候调用 dispatch,可能在promise 函数执行完或在 action 外面执行回调函数后,这就须要对旧的 dispatch 函数进行从新包装,让它可能先执行中间件函数外面的办法,并把真正的 dispatch 传递給中间件函数:

let storeDispatch = stroe.dispatch;// 取出 store 的 dispatch 办法保留

// 重写 dispatch 办法
stroe.dispatch = function (action) {console.log('before dispatch')
  stroe.dispatch(action) // 在适当的时候调用实在的 dispatch 办法
  console.log('after dispatch')
}

只有一个中间件

logger 中间件

咱们能够写一个 logger 中间件来进一步理解:

function logger(store){return (dispatch)=>{return (action)=>{console.log("before logger");
        store.dispatch(action);
        console.log('after logger')
    }
  }
}

中间件是一个柯里化组合的函数,每个层级都包装有对应的函数参数供咱们调用,真正执行的 dispatch 办法其实在最初一个返回的函数外面。

applyMiddleware

扯了这么多还没看看真正的 applyMiddleware 函数长什么样子:

function applyMiddleware(middlewares) { // @params 中间件数组
  return function (createStore) { // @params 创立 store 函数
    return function (reudcer) { // @params reducer
        return store; // @return 返回 createStore(reducer)
    }
  }
}

applyMiddleware也是一个柯里化组合的函数,不过最终返回的是一个 store,照下面说的,redux 中间件解决的是 dispatch 办法,这里也把 storedispatch办法从新包装一下:

function applyMiddleware(middleware) {return function (createStore) {return function (reudcer) {let store = createStore(reudcer);
      let dispatch = ()=>{throw new Error("dispatch 还不能用,还没革新成 next 办法")};
      dispatch = middleware(store)(store.dispatch); // 革新包装后的 dispatch; 对应 logger(store)(dispatch)
      return {
        ...store,
        dispatch
      }
    }
  }
}

// 依据 applyMiddleware 须要返回的函数传入对应的值
applyMiddleware(logger)(createStore)(reducer);

这样每次在组件中调用 storedispatch办法时,其实调用的是通过中间件 logger 包装后的dispatch

function dispatch(action){console.log('before logger')
  store.dispatch(action) // 这里才是实在调用 store.dispatch;
  console.log('after logger')
}

多个中间件

多个中间件的状况比较复杂,须要保障每个中间件办法都能执行,并且可能像洋葱模型一样,dispatch办法可能在最外面执行:

function applyMiddleware(...middlewares) {return function (createStore) {return function (reudcer) {const store = createStore(reudcer)
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      }
      
      // 将中间件函数先遍历,执行外面的办法并返回,失去一个 dispatch 办法的中间件数组
      const chain = middlewares.map((middleware) => middleware(middlewareAPI))
      
      // compose 函数会将蕴含了 dispatch 办法的中间件数组组合成一个嵌套的包装函数返回
      const dispatch = compose(...chain)(store.dispatch)
      
      return {
        ...store,
        dispatch,
      }
    }
  }
}

这里 compose 办法把中间件外面的 dispatch 办法给包装到了一起,让中间件可能一层层往里执行:

//compose
function compose(...funcs) {if (funcs.length === 0) {return (args) => args
  }
  if (funcs.length === 1) {return funcs[0]
  }
  return funcs.reduce((a, next) => (...args) => a(next(...args)))
}

这里的 reduce 不好了解,把它拆成函数,可能会好了解一点:

function compose(...middlewarw) {return function(storeDispatch) {function dispatch(index, action) {let fn = middlewarw[index]
      
      // 回调外面的 next 办法,执行的是下一个中间件函数
      let next = () => dispatch(index + 1, action)
      // 如果还有下一个中间件就继续执行,并把 action 传进去,否则执行 store.dispatch 办法
      fn ? fn(next)(action) : storeDispatch(action)
    }

    return (action) => dispatch(0, action)
  }
};

罕用中间件

另外罕用到的中间件,这里也写下它的源码实现:

redux-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;

redux-promise

function isPromise(obj) {return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
export default function promiseMiddleware({dispatch}) {
    return 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);
    };
}

正文完
 0