理解redux中间件

Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。比如日志中间件logger可以在你每次dispatch派发动作的时候打印新旧的状态。

 

通常要实现这一的功能我们也可以在代码中调用dispatch的地方手动记录状态即可,而没必要使用中间件。

let action = addTodo('Use Redux')
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())

 

虽然这样做达到了想要的效果,但是你并不想每次都这么干。

 

于是你将上面的操作抽取成一个函数:

function logger(store, action) {
  console.log('dispatching', action)
  store.dispatch(action)
  console.log('next state', store.getState())
}

 

然后用它替换 store.dispatch():

logger(store, addTodo('Use Redux'))

 

但是每次都要导入一个外部方法总归还是不太方便。

 

于是你想到重写dispatch函数,直接替换 store 实例中的 dispatch 函数

let next = store.dispatch
store.dispatch = function logger(action) {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

 

无论我们在哪里发起 action,保证都会被记录。如果有多个中间件,你也可以封装成函数,然后分别调用

function logger(store) {
  let next = store.dispatch
  store.dispatch = function (action) {
    console.log('dispatching', action)
    let result = next(action)
    console.log('next state', store.getState())
    return result
  }
}
function report(store) {
  let next = store.dispatch
  store.dispatch = function (action) {
    try {
      return next(action)
    } catch (err) {
      console.error('捕获一个异常!', err)
      throw err
    }
  }
}
logger(store)
report(store)

 

在之前,我们用自己的函数替换掉了 store.dispatch。如果我们不这样做,而是在函数中返回新的 dispatch 呢?

function logger(store) {
  let next = store.dispatch
  // 我们之前的做法:
  // store.dispatch = function (action) {
  return function (action) {
    console.log('dispatching', action)
    let result = next(action)
    console.log('next state', store.getState())
    return result
  }
}
function report(store) {
  let next = store.dispatch
  return function (action) {
    try {
      return next(action)
    } catch (err) {
      console.error('捕获一个异常!', err)
      throw err
    }
  }
}
function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()
  // 在每一个 middleware 中变换 dispatch 方法。
  middlewares.forEach(middleware =>
    store.dispatch = middleware(store)
  )
}
applyMiddleware(store, [ logger, report ])

 

以让 middleware 以方法参数的形式接收一个 next() 方法,而不是通过 store 的实例去获取。

function logger(store) {
  // 我们之前的做法:
  // let next = store.dispatch
  return function (next) {
    return function (action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}

 

再次修改applyMiddleware函数

function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()
  let dispatch = store.dispatch
  middlewares.forEach(middleware =>
    dispatch = middleware(store)(dispatch)
  )
}

 

最后源码

function applyMiddleWare(...middlewares) {
  return function (createStore) {
    return function (reducer) {
      let store = createStore(reducer)
      let dispatch = () => {
        throw new Error('现在还不能用')
      }
      let middleAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args)
      }
      const chain = middlewares.map(middleware => middleware(middleAPI))
      function compose (...funcs) {
        if (funcs.length === 0) {
          return args => args
        }
        if (funcs.length === 1) {
          return funcs[0]
        }
        return funcs.reduce((a, b) => (...args) => a(b(...args)))
      }
      dispatch = compose(...chain)(store.dispatch)
      return {
        ...store,
        dispatch
      }
    }
  }
}

 

注意compose如何执行中间件的,你就明白为什么叫next,因为next指向下一个中间件函数

function fn1(p) {
  console.log(p, 'fn1')
}
function fn2(p) {
  console.log(p, 'fn2')
}
function compose (arr) {
  return arr.reduce((a, b) => (...args) => a(b(...args)))
}
let dispatch = compose([fn1, fn2])
console.log(fn(0)) 
// 0 fn2 
// 10 fn1 
// 30

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.duanlonglong.com/qdjy/695.html