Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redux源码分析(4) — applyMiddleware #36

Open
dark9wesley opened this issue May 19, 2022 · 0 comments
Open

redux源码分析(4) — applyMiddleware #36

dark9wesley opened this issue May 19, 2022 · 0 comments

Comments

@dark9wesley
Copy link
Owner

applyMiddleware.js

applyMiddleware帮助我们将第三方的中间件,转换成能增强store的enhancer。

下面是一个运用redux-thunk的例子:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';

const store = createStore(reducer, applyMiddleware(thunk));

export store

可以看到applyMiddleware接收中间件,执行返回的结果就是一个能增强store的enhancer。

还记得在createStore.js里有这么一段代码吗?:

if(typeof enhancer !== undefined){
  if(typeof enhancer !== 'function'){
    throw new Error('Expected the enhancer to be a function.');
  }

  return enhancer(createStore)(reducer, preloadedState)
}

可以看到,在createStore中,如果发现传入了合法的enhancer,就会将逻辑交给enhancer执行,让它返回增强后的store。

结合两段代码,我们可以推测出applyMiddleware函数的大致模样:

// 第一层,传入中间件,返回一个enhancer,供createStore使用
function applyMiddleWare(...middlewares){

  // 第二层,供createStore内部调用
  return (createStore) => {

    // 第三层,供createStore内部调用
    return (reducer, preloadedState) => {

      // ....具体逻辑

      return store; // 返回增强后的store
    }
  }
}

下面我们看一下applyMiddleware.js里的具体源码:

import { compose } from 'redux'

export default function applyMiddleware(...middlewares){
  return (createStore) => {
    return (reducer, preloadedState) => {
      const store = createStore(reducer, preloadedState)
      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: (action, ...args) => dispatch(action, ...args)
      }

      const chain = middlewares.map(middleware => middleware(middlewareAPI))

      dispatch = compose(...chain)(store.dispatch)

      return {
        ...store,
        dispatch
      }
    }
  }
}

可以看到,这个函数做了以下几件事情:

  1. 调用传入的createStore,构建一个基础的store
  2. 定一个dispatch方法,这个方法被调用就会报错,注意这个方法在下面会被改写!
  3. 定义一个middlewareAPI对象,里面包含getState方法和会报错的dispatch方法
  4. 使用map方法,将中间件都执行一次,参数是上一步的middlewareAPI对象,得到chain数组
  5. 使用compose方法,将chian数组中的所有函数进行组合,得到一个函数,并赋值给dispatch(改写dispatch)
  6. 返回增强后的store

下面重点讲解会让人迷惑的第4和第5步。

第4步:使用map方法,将中间件都执行一次,参数是上一步的middlewareAPI对象,得到chain数组

为了方便理解,我们这里引入redux-thunk,从头开始分析,下面是redux-thunk的源码:

function createThunkMiddleware(extraArgument?) {
  return function  ({ dispatch, getState }) {
   return function (next) {
     return function (action)  {
       if (typeof action === 'function') {
         return action(dispatch, getState, extraArgument)
       }
        return next(action)
      }
    }
  }
}

const thunk = createThunkMiddleware()

export default thunk

可以看到导出的thunk是createThunkMiddleware()执行的结果,所以我们得到的thunk是:

const thunk = function ({ dispatch, getState }) {
  return function (next) {
    return function (action)  {
      if (typeof action === 'function') {
        return action(dispatch, getState)
      }
      return next(action)
    }
  }
}

当applyMiddleware(thunk)时,applyMiddleware内部得到的middlewares就是:

middlewares = [thunk]

当到第4步,会使用map方法,将中间件都执行,并传入middlewareAPI, 也就是

const chian = [thunk].map(middleware => middleware(middlewareAPI))

这里会执行thunk, 并传入dispatch和getState,执行后,会得到这么一个函数:

const chainThunk = function (next){
  return function (action)  {
    if (typeof action === 'function') {
      return action(dispatch, getState)
    }
    return next(action)
  }
}

所以我们可以知道chain的值:

chain = [chainThunk] 

第5步:使用compose方法,将chian数组中的所有函数进行组合,得到一个函数,并赋值给dispatch(改写dispatch)

redux源码分析(3) — compose里,我们已经讲解了compose的作用。

我们将dispatch = compose(...chain)(store.dispatch)分成两部分执行

  1. 第一步
// 上一步得到的chain
chian = [chainThunk]

compose(...chain)

compose执行后,我们可以得到这么一个函数, 我们将它命名为composeThunk:

const composeThunk = (...args) => chianThunk(...args)
  1. 第二步
dispatch = composeThunk(store.dispatch)

其实注意观察,这里我们调用的composeThunk,本质上也是chianThunk, 所以实际上:

const chainThunk = function (next){
  return function (action)  {
    if (typeof action === 'function') {
      return action(dispatch, getState)
    }
    return next(action)
  }
}

dispatch = chainThunk(store.dispatch)

所以我们得到的新dispatch就是:

function (action)  {
  if (typeof action === 'function') {
    return action(dispatch, getState)
  }
  return next(action)
}

在applyMiddleware的最后:

return {
  ...store,
  dispatch
}

会返回一个被强化了dispatch的store,所以所谓的enhancer,其实就是用来强化store的dispatch的。

最后,还有需要补充的是,我们这里只举例了使用一个中间件的情况,其实大多数项目中,我们都会有多个中间件。

多个中间件的情况

假如我们有以下两个中间件:

const middleware1 = ({dispatch, getState}) => {
  return (next) => {
    return (action) => {
      ...do something
      next(action)
    }
  }
}

const middleware2 = ({dispatch, getState}) => {
  return (next) => {
    return (action) => {
      ...do something
      next(action)
    }
  }
}

我们会在第4步后,得到这么两个函数:

const chain1 = function (next) => {
  return (action) => {
      ...do something
      next(action)
  }
}

const chain2 = function (next) => {
  return (action) => {
      ...do something
      next(action)
  }
}

// chain
chain = [chain1, chain1]

当这个chain被第5步compose(...chain)(store.dispatch)调用时,实际情况如下

compose(...chain)(store.dispatch)
等价于
compose(chain1, chain2)(store.dispatch)
等价于
chain1(chain2(store.dispatch))

最后增强后的dispatch就是上方代码的执行结果,每个中间件最后返回的(action) => ...都会成为下一个中间件的next参数。

所以,当一个中间件做完事情后,就可以调用next,将action传递给下一个中间件。

最后贴一下redux-thunk的带注释代码,帮助理解中间件:

// 第一层 返回一个接受dispatch 和 getState的函数
// 这层供applyMiddleware里传入middlewareAPI调用
const thunk =  ({ dispatch, getState }) => {

  // 第二层 返回一个接受next的函数
  // 这层供compose函数调用
  // next参数是上一层的(action) => ....
  return function (next) {

    // 第三层 返回一个接受action的函数
    // 这层也就是下一个中间件的next参数
    // 当只有一个中间件时,这层就是被增强后的dispatch
    return function (action)  {

      // 如果action类型是函数的话,执行它
      if (typeof action === 'function') {
        return action(dispatch, getState)
      }

      // 不是函数的类型,交给下一个中间件执行
      return next(action)
    }
  }
}

export default thunk

结语

applyMiddleware是redux中最为复杂的一个函数了,虽然代码量不多,但是运用闭包的技巧和函数式思想让人折服。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant