zlx362211854 / daily-study

每日一个知识点总结,以issue的形式体现
10 stars 6 forks source link

91. 说说redux中间件 #147

Open zlx362211854 opened 4 years ago

goldEli commented 4 years ago

redux 中改变 state 的基本步骤是,dispatch 触发 action,然后通过 reducer 重新计算新的 state。

中间件本质上就是为 dispatch 这个方法添加一些附加操作,比如打印日志,异步操作等。

以日志中间件为例,为每个 dispatch 加日志:

// 基础版 dispatch
store.dispatch({type: "doSomething"})

// 为 dispatch 赋予日志功能

const dispatchTemp = store.dispatch

store.dispatch = function(action) {
    console.log("当前 action 为:", action)
    dispatchTemp(action)
}

store.dispatch({type: "doSomething"})
nanslee commented 4 years ago
zlx362211854 commented 4 years ago

redux中间件本质就是对store.dispatch方法的重写或者说扩展。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

上面代码,就是对store.dispatch方法的简单扩展。

redux提供了内置方法applyMiddleware对store.dispatch进行扩展

import {createStore, applyMiddleware, compose} from "redux"
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {...store, dispatch}
  }
}

上面代码,对store.dispatch进行了扩展,关键在这一步dispatch = compose(...chain)(store.dispatch);

关键的compose函数

看看redux源码的compose实现:


/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

他借助了Array的reduce函数来实现,funcs列表里面的函数,都会被倒序依次调用。

看下面例子:

const f1 = (...arg) => {
  console.log(arg, 'arg1')
  return 1
}

const f2 = (...arg) => {
  console.log(arg, 'arg2')
  return 2
} 

const f3 = (...arg) => {
  console.log(arg, 'arg3')
  return 3
}

compose(f1, f2, f3)('a', 'b', 'c') 
// ["a", "b", "c"] "arg3"
// [3] "arg2"
// [2] "arg1"

拆分开来,他的执行过程是这样的:

funcs = [f1, f2, f3]
funcs.reduce((a, b) => (...args) => a(b(...args)))
// reduce执行过程
// loop1: a = f1, b = f2, ret1 = (...args) => f1(f2(...args))
// loop2: a = ret1, b = f3, ret2 = (...args) => ret1(f3(...args)) 
// 即 ret2 = (...args) => f1(f2(f3(...args)))

可见将函数列表像一个洋葱一样包起来执行。 所以我们可以自定义很多middleWare

store = createStore(
          reducers,
         applyMiddleware(ReduxThunk, logger, oth1, ...),
        )

只需要我们按照规则定义:

const logger = store => next => action => {
  console.log('prev state',store.getState())
  console.log('dispatch',action);

  let result = next(action);

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

  return result;
}