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办法给包装到了一起,让中间件可能一层层往里执行:

//composefunction 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);    };}