• Redux源码学习笔记


    https://github.com/reduxjs/redux 版本 4.0.0

    先了解一下redux是怎么用的,此处摘抄自阮一峰老师的《Redux 入门教程

    // Web 应用是一个状态机,视图与状态是一一对应的
    // 所有的状态,保存在一个对象里面
    
    // store 是保存数据的地方
    
    // 创建 store
    import { createStore } from 'redux'
    const store = createStore(fn)
    
    // state 是某一时刻 store 的快照,一个 state 对应一个 view
    // 可通过 getState() 获取
    const state = store.getState()
    
    // Action 是一个对象 用来表示 view 发出的改变 state 的通知
    // type 是必须的 其他属性可以自由设置
    const action = {
        type: 'ADD_TODO',
        payload: 'Learn Redux'
    }
    
    // 同一种类型的 action 可以写一个函数生成
    const ADD_TODO = '添加 TODO'
    // 生成 action 的函数: Action Creator
    function addTodo(text) {
        return {
            type: ADD_TODO,
            text
        }
    }
    
    const action = addTodo('Learn Redux')
    
    // store.dispatch()是 View 发出 Action 的唯一方法。
    store.dispatch(action)
    
    // reducer 是 store 接收 state 返回新的 state 的过程
    
    const defaultState = 0
    // reducer 接收 action 返回新的 state
    const reducer = (state = defaultState, action) => {
        switch(action.type) {
            case: 'ADD':
                return state + action.payload
            default:
                return state
        }
    }
    const state = reducer(1, {
        type: 'ADD',
        payload: 2
    })
    
    // 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer
    const store = createStore(reducer)
    
    /*
    reducer 是一个纯函数,纯函数要求:
    - 不得改写参数
    - 不能调用系统 I/O 的API
    - 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
    */
    
    // store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数
    // 返回解除监听函数
    let unsubscribe = store.subsribe(() => { console.log(store.getState) })
    unsubscribe() // 解除监听
    
    /*
    store 提供的三个方法
    - store.getState()
    - store.dispatch()
    - store.subscribe()
    */
    
    // createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。
    // !这个初始值会覆盖 Reducer 函数默认的初始值
    let store = createStore(todoApp,  STATE_FROM_SERVER)
    
    // createStore 的简单实现
    const createStore = (reducer) => {
        let state
        let listeners = []
    
        const getState = () => state
    
        const dispatch = action => {
            state = reducer(state, action)
            listeners.forEach(listener => listener())
        }
    
        const subscribe = listener => {
            listeners.push(listener)
            return () => {
                listeners = listeners.filter(l => l !== listener)
            }
        }
    
        dispatch({})
    
        return { getState, dispatch, subscribe }
    
    }
    
    // 可以通过 combineReducers 来将多个 Reducer 合为一个
    import { combineReducers } from 'redux'
    
    const chatReducer = combineReducers({
        chatLog,
        statusMessage,
        userName
    })
    
    // combineReducer 的简单实现
    const combineReducers = reducers => {
        return (state = {}, action) => 
            Object.keys(reducers).reduce(
                (nextState, key) => {
                    nextState[key] = reducers[key](state[key], action)
                    return nextState
                },
                {}
            )
    }

    工作流程

    Redux Flow
    
            dispatch(action)   (previousState, action)
    Action Creators ======> Store ======> Reducers
       ^                     ||   <======
        \_                   ||   (newState)
          \_         (state) ||
            \_               ||
    (view opt)\_             /
                ---   React Comonents

    OK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh 

    combineReducers.js 

    代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....

    思路就是创建一个对象 将 Reducer 全部放进去

    当Action传进来的时候 就让每一个Reducer去处理这个action

    每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略

    截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接

    function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
    
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      const finalReducerKeys = Object.keys(finalReducers)
    
    
      return function combination(state = {}, action) {
        let hasChanged = false
        const nextState = {}
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]
          const nextStateForKey = reducer(previousStateForKey, action)
    
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        // 如果state每一个key都没有被修改 就直接返回原state
        return hasChanged ? nextState : state
      }
    }
    
    /***************** 下面是简单的用法实例 *****************/
    function todos(state = [], action) {
      switch (action.type) {
        case 'ADD_TODO':
          return state.concat(action.text)
        default:
          return state
      }
    }
    
    function counter(state = 0, action) {
      switch (action.type) {
        case 'INCREMENT':
          return state + 1
        case 'DECREMENT':
          return state - 1
        default:
          return state
      }
    }
    
    let reducer = combineReducers({ list: todos, number: counter })
    let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
    console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
    state = reducer(state, { type: 'ADD_TODO', text: 'study' })
    console.log(state) // { list: [ 'study' ], number: 0 }
    state = reducer(state, { type: 'ADD_TODO', text: 'sleep' })
    console.log(state) // { list: [ 'study', 'sleep' ], number: 0 }
    state = reducer(state, { type: 'INCREMENT' })
    console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }

    combineReducers.js 源码

    import ActionTypes from './utils/actionTypes'
    import warning from './utils/warning'
    import isPlainObject from './utils/isPlainObject'
    
    function getUndefinedStateErrorMessage(key, action) {
      const actionType = action && action.type
      const actionDescription =
        (actionType && `action "${String(actionType)}"`) || 'an action'
    
      return (
        `Given ${actionDescription}, reducer "${key}" returned undefined. ` +
        `To ignore an action, you must explicitly return the previous state. ` +
        `If you want this reducer to hold no value, you can return null instead of undefined.`
      )
    }
    
    function getUnexpectedStateShapeWarningMessage(
      inputState,
      reducers,
      action,
      unexpectedKeyCache
    ) {
      const reducerKeys = Object.keys(reducers)
      const argumentName =
        action && action.type === ActionTypes.INIT
          ? 'preloadedState argument passed to createStore'
          : 'previous state received by the reducer'
    
      if (reducerKeys.length === 0) {
        return (
          'Store does not have a valid reducer. Make sure the argument passed ' +
          'to combineReducers is an object whose values are reducers.'
        )
      }
    
      if (!isPlainObject(inputState)) {
        // 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的
        // [object Array] 中提取 'Array'
        // Object.prototype.toString.call(inputState).match(/s([a-z|A-Z]+)/)[1]
        return (
          `The ${argumentName} has unexpected type of "` +
          {}.toString.call(inputState).match(/s([a-z|A-Z]+)/)[1] +
          `". Expected argument to be an object with the following ` +
          `keys: "${reducerKeys.join('", "')}"`
        )
      }
      // 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= =
      const unexpectedKeys = Object.keys(inputState).filter(
        key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
      )
    
      unexpectedKeys.forEach(key => {
        unexpectedKeyCache[key] = true
      })
      // 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.REPLACE })
      if (action && action.type === ActionTypes.REPLACE) return
    
      if (unexpectedKeys.length > 0) {
        return (
          `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
          `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
          `Expected to find one of the known reducer keys instead: ` +
          `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
        )
      }
    }
    
    function assertReducerShape(reducers) {
      Object.keys(reducers).forEach(key => {
        const reducer = reducers[key]
        const initialState = reducer(undefined, { type: ActionTypes.INIT })
          // Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。
          // 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是undefined。
        if (typeof initialState === 'undefined') {
          throw new Error(
            `Reducer "${key}" returned undefined during initialization. ` +
              `If the state passed to the reducer is undefined, you must ` +
              `explicitly return the initial state. The initial state may ` +
              `not be undefined. If you don't want to set a value for this reducer, ` +
              `you can use null instead of undefined.`
          )
        }
    
        if (
          typeof reducer(undefined, {
            type: ActionTypes.PROBE_UNKNOWN_ACTION()
          }) === 'undefined'
        ) {
          // 当使用随机类型探测Reducer${key}时返回undefined。
          // 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。
          // 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined,
          // 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null
          throw new Error(
            `Reducer "${key}" returned undefined when probed with a random type. ` +
              `Don't try to handle ${
                ActionTypes.INIT
              } or other actions in "redux/*" ` +
              `namespace. They are considered private. Instead, you must return the ` +
              `current state for any unknown actions, unless it is undefined, ` +
              `in which case you must return the initial state, regardless of the ` +
              `action type. The initial state may not be undefined, but can be null.`
          )
        }
      })
    }
    
    /**
     * Turns an object whose values are different reducer functions, into a single
     * reducer function. It will call every child reducer, and gather their results
     * into a single state object, whose keys correspond to the keys of the passed
     * reducer functions.
     *
     * @param {Object} reducers An object whose values correspond to different
     * reducer functions that need to be combined into one. One handy way to obtain
     * it is to use ES6 `import * as reducers` syntax. The reducers may never return
     * undefined for any action. Instead, they should return their initial state
     * if the state passed to them was undefined, and the current state for any
     * unrecognized action.
     *
     * @returns {Function} A reducer function that invokes every reducer inside the
     * passed object, and builds a state object with the same shape.
     */
    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
    
        if (process.env.NODE_ENV !== 'production') {
          if (typeof reducers[key] === 'undefined') {
            warning(`No reducer provided for key "${key}"`)
          }
        }
    
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      const finalReducerKeys = Object.keys(finalReducers)
    
      let unexpectedKeyCache
      if (process.env.NODE_ENV !== 'production') {
        unexpectedKeyCache = {}
      }
    
      let shapeAssertionError
      try {
        // 判断每个reducer都有初始值和对于未知action返回原state
        assertReducerShape(finalReducers)
      } catch (e) {
        shapeAssertionError = e
      }
    
      return function combination(state = {}, action) {
        if (shapeAssertionError) {
          throw shapeAssertionError
        }
    
        if (process.env.NODE_ENV !== 'production') {
          const warningMessage = getUnexpectedStateShapeWarningMessage(
            state,
            finalReducers,
            action,
            unexpectedKeyCache
          )
          if (warningMessage) {
            warning(warningMessage)
          }
        }
    
        let hasChanged = false
        const nextState = {}
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]
          const nextStateForKey = reducer(previousStateForKey, action)
          if (typeof nextStateForKey === 'undefined') {
            const errorMessage = getUndefinedStateErrorMessage(key, action)
            throw new Error(errorMessage)
          }
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        // 如果state每一个key都没有被修改 就直接返回原state
        return hasChanged ? nextState : state
      }
    }
    View Code

    utils/actionTypes.js

    // 生成随机字符串的方式可以参考下
    // 随机数转36进制 可以生成 '0-9a-z' 的随机字符串
    const randomString = () =>
      Math.random()
        .toString(36)
        .substring(7)
        .split('')
        .join('.')
    // 私有action类型 (其实就相当于未知的action 返回当前状态就好了
    // 如果当前 state 为undefined 就返回 Reducer设置的初始 state
    const ActionTypes = {
      INIT: `@@redux/INIT${randomString()}`,
      REPLACE: `@@redux/REPLACE${randomString()}`,
      PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
    }
    
    export default ActionTypes

    createStore.js 

    是redux核心代码,不过这个没有什么难理解的地方

    import $$observable from 'symbol-observable'
    
    import ActionTypes from './utils/actionTypes'
    import isPlainObject from './utils/isPlainObject'
    
    // 创建 store 的函数
    // preloadedState: store设置的初始值  这个值会覆盖 Reducer 的默认值
    // 如果使用了 combineReducers preloadedState 要和 combineReducers 有相同的keys
    // enhancer: 中间件
    export default function createStore(reducer, preloadedState, enhancer) {
      // preloadedState可以不传 判断preloadedState是否存在
      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.')
        }
        // enhancer是一个高阶函数 调用enhancer返回一个"加强版"的createStore
        return enhancer(createStore)(reducer, preloadedState)
      }
    
      if (typeof reducer !== 'function') {
        throw new Error('Expected the reducer to be a function.')
      }
    
      let currentReducer = reducer
      let currentState = preloadedState
      let currentListeners = []
      let nextListeners = currentListeners
      let isDispatching = false
      // 判断当前 nextListeners 和 currentListeners 是否为同一个对象
      // 如果是一个对象 就把 nextListeners 改为 currentListeners 的副本
      function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
          nextListeners = currentListeners.slice()
        }
      }
      // 获取当前对象 如果是正在派发action 则不能获取state
      function getState() {
        if (isDispatching) {
          throw new Error(
            'You may not call store.getState() while the reducer is executing. ' +
              'The reducer has already received the state as an argument. ' +
              'Pass it down from the top reducer instead of reading it from the store.'
          )
        }
    
        return currentState
      }
      // 订阅 添加订阅者
      function subscribe(listener) {
        if (typeof listener !== 'function') {
          throw new Error('Expected the listener to be a function.')
        }
    
        if (isDispatching) {
          throw new Error(
            'You may not call store.subscribe() while the reducer is executing. ' +
              'If you would like to be notified after the store has been updated, subscribe from a ' +
              'component and invoke store.getState() in the callback to access the latest state. ' +
              'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
          )
        }
    
        let isSubscribed = true
        // 每次修改 nextListeners 都要判断一下 nextListeners 和 currentListeners 是否为同一个对象
        ensureCanMutateNextListeners()
        // 注意 这里修改 nextListeners 之后并没有改变 currentListeners 而是在下一次用到 currentListeners 才会改变
        nextListeners.push(listener)
    
        // 返回一个当前监听者取消订阅的方法
        return function unsubscribe() {
          if (!isSubscribed) {
            return
          }
          // 正在派发 action 时不能进行操作
          if (isDispatching) {
            throw new Error(
              'You may not unsubscribe from a store listener while the reducer is executing. ' +
                'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
            )
          }
    
          isSubscribed = false
    
          ensureCanMutateNextListeners()
          const index = nextListeners.indexOf(listener)
          nextListeners.splice(index, 1)
        }
      }
    
    
      function dispatch(action) {
        if (!isPlainObject(action)) {
          throw new Error(
            'Actions must be plain objects. ' +
              'Use custom middleware for async actions.'
          )
        }
    
        if (typeof action.type === 'undefined') {
          throw new Error(
            'Actions may not have an undefined "type" property. ' +
              'Have you misspelled a constant?'
          )
        }
    
        if (isDispatching) {
          throw new Error('Reducers may not dispatch actions.')
        }
    
        try {
          // 用 isDispatching 记录是否正在 派发action 过程中不能进行其他操作
          isDispatching = true
          currentState = currentReducer(currentState, action)
        } finally {
          isDispatching = false
        }
        // 用到 listeners 才会修改 currentListeners 以减少修改次数
        const listeners = (currentListeners = nextListeners)
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          listener()
        }
    
        return action
      }
    
      // 替换 Reducer 并派发动作 ActionTypes.REPLACE 相当于对state重新进行初始化
      function replaceReducer(nextReducer) {
        if (typeof nextReducer !== 'function') {
          throw new Error('Expected the nextReducer to be a function.')
        }
    
        currentReducer = nextReducer
        dispatch({ type: ActionTypes.REPLACE })
      }
      // emmmm...看不懂这个 可以参考 https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/
      function observable() {
        const outerSubscribe = subscribe
        return {
          subscribe(observer) {
            if (typeof observer !== 'object' || observer === null) {
              throw new TypeError('Expected the observer to be an object.')
            }
    
            function observeState() {
              if (observer.next) {
                observer.next(getState())
              }
            }
    
            observeState()
            const unsubscribe = outerSubscribe(observeState)
            return { unsubscribe }
          },
    
          [$$observable]() {
            return this
          }
        }
      }
    
      dispatch({ type: ActionTypes.INIT })
    
      return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      }
    }

    bindActionCreators.js

    此处参考 《mapStateToProps,mapDispatchToProps的使用姿势

    按注释上说 这只是一个 convenience method

    你可以把 store.dispatch(MyActionCreators.doSomething()) 换成一个转成一个函数

    我们使用 action 时 是先通过 actionCreator创建action 然后通过 dispatch 派发出去

    通过 bindActionCreator(actionCreator, dispatch) 获得一个可以直接创建action并派发的函数

    bindActionCreators 就是创建一个对象 每个属性都是一个 可以直接创建action并派发的函数

    例:

    action.increase = (info) => { type:'INCREASE',info }
    action.decrease = (info) => { type:'DECREASE',info }
    
    bindActionCreators({
    increase: action.increase,
    decrease: action.decrease
    }, dispatch)
    
    // 就可以获得:
    {
    increase: (...args) => dispatch(action.increase(...args)),
    decrease: (...args) => dispatch(action.decrease(...args))
    }

    源码:

    function bindActionCreator(actionCreator, dispatch) {
      return function() {
        return dispatch(actionCreator.apply(this, arguments))
      }
    }
    
    /**
     * Turns an object whose values are action creators, into an object with the
     * same keys, but with every function wrapped into a `dispatch` call so they
     * may be invoked directly. This is just a convenience method, as you can call
     * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
     *
     * For convenience, you can also pass a single function as the first argument,
     * and get a function in return.
     *
     * @param {Function|Object} actionCreators An object whose values are action
     * creator functions. One handy way to obtain it is to use ES6 `import * as`
     * syntax. You may also pass a single function.
     *
     * @param {Function} dispatch The `dispatch` function available on your Redux
     * store.
     *
     * @returns {Function|Object} The object mimicking the original object, but with
     * every action creator wrapped into the `dispatch` call. If you passed a
     * function as `actionCreators`, the return value will also be a single
     * function.
     */
    export default function bindActionCreators(actionCreators, dispatch) {
      // 如果 actionCreators 是一个函数 说明只有一个 actionCreator
      if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    
      if (typeof actionCreators !== 'object' || actionCreators === null) {
        throw new Error(
          `bindActionCreators expected an object or a function, instead received ${
            actionCreators === null ? 'null' : typeof actionCreators
          }. ` +
            `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
        )
      }
    
      const keys = Object.keys(actionCreators)
      const boundActionCreators = {}
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const actionCreator = actionCreators[key]
        if (typeof actionCreator === 'function') {
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
      }
      return boundActionCreators
    }

    applyMiddleware.js

    这个应该是最难理解的部分 所以放到最后看>_<

    个人理解,这个东西就是在dispatch前后做一些事情=.= 类似koa express的中间件嘛

    以下参考 源码中 redux/docs/advanced/Middleware.md

    middleware 在dispatch和action之间提供一个第三方程序扩展点。

    现在一步一步理解applyMiddleware在做什么

    首先,假设现在有一个需求,每次dispatch一个action时,都要打印action和state,像下面这样:

    const action = addTodo('Use Redux')
    
    console.log('dispatching', action)
    store.dispatch(action)
    console.log('next state', store.getState())

    但是不可能每一次都这样打印,也许直接修改dispatch就可以

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

    呐,可能不止一个需求,现在我又想记录错误信息了。我们写两个方法,分别给dispatch添加自己想要的功能。

    function patchStoreToAddLogging(store) {
      const next = store.dispatch
      store.dispatch = function dispatchAndLog(action) {
        console.log('dispatching', action)
        let result = next(action)
        console.log('next state', store.getState())
        return result
      }
    }
    
    function patchStoreToAddCrashReporting(store) {
      const next = store.dispatch
      store.dispatch = function dispatchAndReportErrors(action) {
        try {
          return next(action)
        } catch (err) {
          console.error('Caught an exception!', err)
          Raven.captureException(err, {
            extra: {
              action,
              state: store.getState()
            }
          })
          throw err
        }
      }
    }
    
    patchStoreToAddLogging(store)
    patchStoreToAddCrashReporting(store)

    但是这样并不好……很明显,我们在修改store的私有属性了,emmm……这是一个比较hack的方法……要改的优雅一点,把修改dispatch的部分封装起来。每一次返回新的dispatch,修改store的部分由 applyMiddlewareByMonkeypatching 统一处理。

    function logger(store) {
      const next = store.dispatch
      // Previously:
      // store.dispatch = function dispatchAndLog(action) {
      return function dispatchAndLog(action) {
        console.log('dispatching', action)
        let result = next(action)
        console.log('next state', store.getState())
        return result
      }
    }
     
    function applyMiddlewareByMonkeypatching(store, middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
      // Transform dispatch function with each middleware.
      middlewares.forEach(middleware =>
        store.dispatch = middleware(store)
      )
    }
    
    applyMiddlewareByMonkeypatching(store, [logger, crashReporter])

    但是这样还是不太好。dispatch是store的私有属性,我们却直接获取了。思考我们为什么重写dispatch,因为我们在用多个中间件的时候,第一个中间件修改完dispatch,下一次修改应该是在前一个的基础之上,包裹上一次修改的dispatch。但其实,这也不是必要的,只要每一次传入上一次修改后的dispatch就可以了。

    function logger(store) {
      return function wrapDispatchToAddLogging(next) {
        return function dispatchAndLog(action) {
          console.log('dispatching', action)
          let result = next(action)
          console.log('next state', store.getState())
          return result
        }
      }
    }

    这里的next就是之前的中间件处理后的dispatch,我们不再获取store的私有属性了,改为用参数传递。然后在处理之后(logger(store)(next))返回一个新的dispatch。

    为什么这里要套两个函数而不是传入两个参数(store, next)呢,就相当于把这个函数柯里化了嘛……后面可以看到用处。

    改成ES6的箭头函数

    const logger = store => next => action => {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }

    说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个中间件的写法了。

    可以简单的实现下 applyMiddleware

    function applyMiddleware(store, middlewares) {
      middlewares = middlewares.slice()
      middlewares.reverse()
      let dispatch = store.dispatch
      middlewares.forEach(middleware =>
        dispatch = middleware(store)(dispatch)
      )
      return Object.assign({}, store, { dispatch })
    }

    这样就可以最后使用 applyMiddleware

    import { createStore, combineReducers, applyMiddleware } from 'redux'
    
    const todoApp = combineReducers(reducers)
    const store = createStore(
      todoApp,
      // applyMiddleware() tells createStore() how to handle middleware
      applyMiddleware(logger, crashReporter)
    )

    深入(meiyou)的理解之后 开始看applyMiddleware.js源码

    其中用到里 compose 要先看一下

    compose.js

    这个是函数式编程的一个……思想?应用?

    将函数的嵌套调用写成组合  compose(b, c, a) 相当于   b(c(a(x)))

    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
        return funcs[0]
      }
      // reduce的参数.. 
      // reduce(function(accumulator, currentValue, currentIndex, array) {...})
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    
    /**********    使用示例     **********/
    
    let a = x => 'a' + x + 'a'
    let b = x => 'b' + x + 'b'
    let c = x => 'c' + x + 'c'
    let foo = compose(b, c, a)
    console.log(foo('v')) // bcavacb
    let bar = x => b(c(a(x)))
    console.log(bar('v')) // bcavacb

     最后看applyMiddleware.js

    import compose from './compose'
    
    /**
     * Creates a store enhancer that applies middleware to the dispatch method
     * of the Redux store. This is handy for a variety of tasks, such as expressing
     * asynchronous actions in a concise manner, or logging every action payload.
     *
     * See `redux-thunk` package as an example of the Redux middleware.
     *
     * Because middleware is potentially asynchronous, this should be the first
     * store enhancer in the composition chain.
     *
     * Note that each middleware will be given the `dispatch` and `getState` functions
     * as named arguments.
     *
     * @param {...Function} middlewares The middleware chain to be applied.
     * @returns {Function} A store enhancer applying the middleware.
     */
    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
        }
      }
    }

    applyMiddleware([middlewares]) 就是返回一个函数 传入createStore,返回新的createStore,创建的store的dispatch是经过中间件加工的。

    这里可以看到编写中间件嵌套两个函数的用处,先传入一个store,只需要再传入一个最新的dispatch就可以了,就是把dispatch用中间件轮流处理一下。这里使用了compose。

    勉强看完源码。假装自己理解了这样子。

  • 相关阅读:
    通达OA 新旧两种数据库连接方式
    c++ 如何获取系统时间
    性能测试开源小工具——http_load介绍
    http_load安装与测试参数分析
    不错的C++框架: Thrift(2)-传输和网络相关
    管理处理器的亲和性(affinity)
    300元内,此耳机是首选。不亏千人好评,对的起你的耳朵。
    [品质生活] 舒适 Schick HYDRO 5剃须刀
    巴氏刷牙法_百度百科
    Amazon.com : The Odyssey of the Manual Toothbrusher
  • 原文地址:https://www.cnblogs.com/wenruo/p/9664375.html
Copyright © 2020-2023  润新知