Redux源码分析之applyMiddleware

Redux源码分析之基本概念

Redux源码分析之createStore

Redux源码分析之bindActionCreators

Redux源码分析之combineReducers

Redux源码分析之compose

Redux源码分析之applyMiddleware 

Redux 最为经典我觉得就是compose 和 applyMiddleware 了。

还是先借一张图,描述的非常准确,

  • 中间件是通过next来进入下一个中间件的,执行完毕后,会调用最原始的store.disptach,reducer执行完毕后,该次操作并没有完毕, 还会依次返回到中间件。
  • 任何一个中间件不next ,其后面的中间件都不会执行,(不等于return next(action),return next(action)一般情况都是返回原始的action, 只要你调用了next(action)就行),redux-thunk就是这么干的(检查到action是函数的时候,没有执行next())

 那么我们还是来看一个简单的例子,这里我把redux-thunk的核心代码直接copy过来,放在一起了。

// thunk 中间件
let thunk = ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
        return action(dispatch, getState)
    }
    return next(action)
}
// logger中间件
let logger = ({ dispatch, getState }) => next => action => {
    console.log('next:之前state', getState())
    let result = next(action)
    console.log('next:之前state', getState())
    return result
}

let { createStore, applyMiddleware } = self.Redux
let todoList = [] // 默认值
let todoReducer = function (state = todoList, action) {
    switch (action.type) {
        case 'add':
            return [...state, action.todo]
        case 'delete':
            return state.filter(todo => todo.id !== action.id)
        default:
            return state
    }
}
let addAsync = content => (dispatch) => {
    setTimeout(function () {
        dispatch({
            type: 'add',
            todo: {
                id: new Date().getTime(),
                content
            }
        })
    }, 1000)
}

let store = createStore(todoReducer, applyMiddleware(logger)),
    subscribe1Fn = function () {
        console.log(store.getState())
    }

// 订阅
let sub = store.subscribe(subscribe1Fn)

store.dispatch(addAsync('异步添加的todo哦'))
store.dispatch({
    type: 'add',
    todo: {
        id: 1,
        content: '学习redux'
    }
})

       从上面的例子,我们总结一下

  • 除了有效的更新数据,还通过中间件达到了额外的操作,比如输出日志,能够发异步的action,这就是中间件的神奇之处
  • 这里有异步action,这就是中间件(redux-thunk)的力量
  • 中间件的格式一般都是   ({ dispatch, getState }) => next => action => {......}, 为什是这样先不做分析
  • 执行顺序  中间件 =>订阅=>中间件

  回归源码,applyMiddleware.js,先删除一些代码,很容易理解

  • 创建一个store
  • 返回一个改写过dispatch方法的store  
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch  // 存旧的dispatch 
.........
/生成新的dispatch dispatch = compose(...chain)(store.dispatch) // 返回改写过disptach的store return { ...store, dispatch } } }

applyMiddleware 并不神奇,其他地方都很理解,内外层的参数传递都是围绕着 createStore来的。
那我们也不难理解应该怎么调用这个方法,应该像如下这样

  • 传入 thunk, logger等等各种中间件,
  • 接着传入我们创建store的方法createStore
  • 最后传入reducer,preloadState,enhancer     
let store = applyMiddleware(thunk, logger)(createStore)(todoReducer),

可是回头看看我们的代码

let store = createStore(todoReducer, applyMiddleware(thunk,logger)),

那我们再回来看看createStore方法

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

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

    return enhancer(createStore)(reducer, preloadedState)
  }
 .......
}
  • 如果 preloadState是函数,并且enhancer为空, enhancer =preloadState
  • 接着,如果有enhancer ,那么 return enhancer(createStore)(reducer, preloadedState) 

结合我们的调用分析一下

createStore(todoReducer, applyMiddleware(thunk,logger))

  • preloadState是函数,并且enhancer为空, enhancer  =  applyMiddleware(thunk,logger)
  • return enhancer(createStore)(reducer, preloadedState) =  return applyMiddleware(thunk,logger)(createStore)(reducer, preloadedState) 

所以嘛,createStore(todoReducer, applyMiddleware(thunk,logger)) 只是一种变体,更加方便调用而已,这里也正式了 applyMiddleware(thunk,logger) 也是store的一个增强函数

 

 我们最后在看看看我们忽略的代码

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {  // createStore就是redux公布的方法creatStore,
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch  // 存旧的dispatch
    let chain = []

      /*
      中间件标准格式
      let logger1 = ({ dispatch, getState }) => next => action => {
        ...     
        let result = next(action)
        ...
        return result
      }
      */
     
    //构建参数 ({dispatch, getState})     
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    /*
      middleware(middlewareAPI)之后是这样的格式 
      let m =  next => action => {
        ...     
        let result = next(action)
        ...
        return result
      }
    */
    chain = middlewares.map(middleware => middleware(middlewareAPI))

    //生成新的dispatch
    dispatch = compose(...chain)(store.dispatch)
   // 返回改写过disptach的store
    return {
      ...store,
      dispatch
    }
  }
}

我滴个神,就三句,第一句,不多说了,给所有的中间件,初始化参数,

这也就是为什么,所有中间件的格式都是第一层参数都是 {disptach,getState}这个样子

 ({dispatch, getState} ) => next => action => {......}

第二句就是把每个middleware传入参数,初始化一下,这里的最大作用就是利用闭包,让每个middleware拥有同一份disptach和getState的引用。

执行后,每个middleware返回的函数式这个样子的, chain保存着这种函数的集合

next => action => {
    ...     
    let result = next(action)
    ...
     return result
}

剩下最核心的一句

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

compose之前已经详细的解读过了,就是生成链式的调用,是把  f,g,h  变成 (...args) => f(g(h(...args))),现在f,g,h的格式如下,

next => action => {
    ...     
    let result = next(action)
    ...
     return result
}

可想而知,这样的函数是整个作为前面一个函数的next参数存在的,所以你每次next(action)实际上就是进入下一个中间件的执行体,

接着把 store.dispatch 作为next参数传入,作为了最内层,也是最后一个中间件的next,返回的函数格式就是下面这个样子了,我们替换一下参数

action => {
    ...     
    let result = next(action)
    ...
     return result
}

等于

action => {
    ...     
    let result = store.dispatch(action)  // 真正的dispatch action
    ...
     return result
}

这个最后庞大的函数被赋值给了store,替换掉了原来的dispatch。整体就是这个样子拉。 

posted @ 2017-08-04 15:14  -云-  阅读(1611)  评论(0编辑  收藏  举报