十分钟理解Redux中间件

栏目: IOS · Android · 发布时间: 5年前

内容简介:由于一直用业界封装好的如首先简单提下什么是中间件,该部分与下文关系不大,可以跳过。来看眼这个经典的图。

由于一直用业界封装好的如 redux-logger、redux-thunk 此类的中间件,并没有深入去了解过 redux 中间件的实现方式。正好前些时间有个需求需要对 action 执行时做一些封装,于是借此了解了下 Redux Middleware 的原理。

* 中间件概念

首先简单提下什么是中间件,该部分与下文关系不大,可以跳过。来看眼这个经典的图。

十分钟理解Redux中间件

不难发现:

  1. 不使用 middleware 时,在 dispatch(action) 时会执行 rootReducer ,并根据 actiontype 更新返回相应的 state
  2. 而在使用 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 指向

// 储存原来的dispatch
const dispatch = store.dispatch;
// 改变dispatch指向
store.dispatch = dispatchPre;
// 重命名
const next = dispatch;

截止到这我们已经了解了中间件的基本原理了~

源码分析

了解了基本原理能有助于我们更快地读懂 middleware 的源码。一般我们会这样添加中间件并使用。

createStore(rootReducer, applyMiddleware.apply(null, [...middlewares]))

接下来我们可以重点关注这两个函数 createStoreapplyMiddleware

CreateStore

// 摘至createStore
export 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) }
]

可以发现已经将所有 middleware 串联起来了,于是我在之后执行 dispatch 时,就是一个合并了所有中间件的 dispatchFun

最后看一下这时候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)
*/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms for Image Processing and Computer Vision

Algorithms for Image Processing and Computer Vision

Parker, J. R. / 2010-12 / 687.00元

A cookbook of algorithms for common image processing applications Thanks to advances in computer hardware and software, algorithms have been developed that support sophisticated image processing with......一起来看看 《Algorithms for Image Processing and Computer Vision》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具