• 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。整体就是这个样子拉。 

  • 相关阅读:
    mac下使用brew安装mongodb
    从零构建vue+webpack (一)
    常用软件集合(2018/08/22)
    solr集群安装部署
    zookeeper集群部署
    redis集群部署
    linux 安装jdk
    zTab layui多标签页组件
    spring boot集成swagger2
    SSH客户端,FinalShell服务器管理,远程桌面加速软件,支持Windows,Mac OS X,Linux,版本2.6.3.1
  • 原文地址:https://www.cnblogs.com/cloud-/p/7284136.html
Copyright © 2020-2023  润新知