当前位置: 代码迷 >> 综合 >> 8 Middleware(中间件)
  详细解决方案

8 Middleware(中间件)

热度:19   发布时间:2024-01-29 22:41:43.0

理解 Middleware

正因为 middleware 可以完成包括异步 API 调用在内的各种事情。我们将以记录日志和创建崩溃报告为例,引导你体会从分析问题到通过构建 middleware 解决问题的思维过程。

 

问题: 记录日志

每次state更新时,如何记录其值

尝试 #1: 手动记录

let action = addTodo('Use Redux')

 

console.log('dispatching', action)

store.dispatch(action)

console.log('next state', store.getState())

 

尝试 #2: 封装 Dispatch

你可以将上面的操作抽取成一个函数:

function dispatchAndLog(store, action) {

  console.log('dispatching', action)

  store.dispatch(action)

  console.log('next state', store.getState())

}

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

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

 

尝试 #3: Monkeypatching Dispatch

重写我们的dispatch

let next = store.dispatch

store.dispatch = function dispatchAndLog(action) {

  console.log('dispatching', action)

  // 调用原来的 dispatch

  let result = next(action)

  console.log('next state', store.getState())

  return result

}

 

问题: 崩溃报告

按照如上想法,我们添加崩溃报告的处理

function patchStoreToAddLogging(store) {

  let next = store.dispatch

  store.dispatch = function dispatchAndLog(action) {

    console.log('dispatching', action)

    let result = next(action)

    console.log('next state', store.getState())

    return result

  }

}

 

function patchStoreToAddCrashReporting(store) {

  let next = store.dispatch

  store.dispatch = function dispatchAndReportErrors(action) {

    try {

      return next(action)

    } catch (err) {

      console.error('捕获一个异常!', err)

      Raven.captureException(err, {

        extra: {

          action,

          state: store.getState()

        }

      })

      throw err

    }

  }}

 

尝试 #4: 隐藏 Monkeypatching

现在我们不直接更改store.dispatch,而是返回一个新的dispatch,此时我们可以称我们的方法为“中间件”

function logger(store) {

  let next = store.dispatch

 

  return function dispatchAndLog(action) {

    console.log('dispatching', action)

    let result = next(action)

    console.log('next state', store.getState())

    return result

  }

}

 

新建一个方法,叫做“应用中间件”,接收 store 和 中间件数组

function applyMiddlewareByMonkeypatching(store, middlewares) {

  middlewares = middlewares.slice()

  middlewares.reverse()

 

  // 在每一个 middleware 中变换 dispatch 方法。

  middlewares.forEach(middleware =>

    // 调用中间件,每个中间件都返回一个新的dispatch

    store.dispatch = middleware(store)

  )

}

 

然后像这样应用多个 middleware:

applyMiddlewareByMonkeypatching(store, [ logger, crashReporter ])

 

尝试 #5: 移除 Monkeypatching

ES6 的箭头函数可以使其 柯里化 ,从而看起来更舒服一些:

const logger = store => next => action => {

  console.log('dispatching', action)

  let result = next(action)

  console.log('next state', store.getState())

  return result

}

 

const crashReporter = store => next => action => {

  try {

    return next(action)

  } catch (err) {

    console.error('Caught an exception!', err)

    Raven.captureException(err, {

      extra: {

        action,

        state: store.getState()

      }

    })

    throw err

  }

}

这正是 Redux middleware 的样子。

Middleware 接收了一个参数 next ,其本身是 dispatch 函数,并返回一个新的 dispatch 函数,返回的函数会被作为下一个 middleware 的 next()

不明白柯里化函数,如下,这样写应该会明白

store => next => action => {...}store => {return (next => {return (action => {...})})}

 

尝试 #6: “单纯”地使用 Middleware

写一个 applyMiddleware 方法替换原来的 applyMiddlewareByMonkeypatching。

// 警告:这只是一种“单纯”的实现方式!// 这 *并不是* Redux 的 API.

function applyMiddleware(store, middlewares) {

  middlewares = middlewares.slice()

  middlewares.reverse()

 

  let dispatch = store.dispatch

  middlewares.forEach(middleware =>

    dispatch = middleware(store)(dispatch)

  )

 

  return Object.assign({}, store, { dispatch })

}

这与 Redux 中 applyMiddleware() 的实现已经很接近了

 

使用Redux的applyMiddleware

Redux的applyMiddleware原理就是我们所编写的applyMiddleware

import { createStore, combineReducers, applyMiddleware } from 'redux'

let todoApp = combineReducers(reducers)

let store = createStore(

  todoApp,

  // applyMiddleware() 告诉 createStore() 如何处理中间件

  applyMiddleware(logger, crashReporter))

 

就是这样!现在任何被发送到 store 的 action 都会经过 logger 和 crashReporter:

// 将经过 logger 和 crashReporter 两个 middleware!

store.dispatch(addTodo('Use Redux'))