AboutFE icon indicating copy to clipboard operation
AboutFE copied to clipboard

11、Redux 源码相关 及 数据管理 store 比较概念

Open CodingMeUp opened this issue 7 years ago • 7 comments

createStore

import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'

/**
 * These are private action types reserved by Redux.
 * For any unknown actions, you must return the current state.
 * If the current state is undefined, you must return the initial state.
 * Do not reference these action types directly in your code.
 * 这是一个Redux保留的私有action类型
 * 用来作为所有未知的动作, 必须返回当前的state
 * 如果当前的state是 undefined, 就应该返回 初始化的state (initial state)
 * 不要在你的代码中直接引用这些action类型
 */
export var ActionTypes = {
  INIT: '@@redux/INIT'
}

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 * 创建一个持有 State树的 Redux store
 * 唯一改变 store中的 state 的方式就是 调用它的 dispatch() 方法
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 * 在项目中应该只有一个 store 对象。
 * 指定 state树中响应 actions 的 不同部分,通过使用 combineReducers 方法,将多个 reducer 方法 合并成一个单一的 reducer 方法
 *
 * createStore接收三个参数:
 *    - reducer 方法  合并的reducer方法,此函数接收action来更新state
 *    - preLoadedState  初始化的state, 这个参数可以不传,如果这个参数是function ,说明 只传了 reducer和enhancer这两个参数
 *    - enhancer
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * 如果这个reducer是使用 combineReducers 来生成的,那这个preloadedState对象就必须与`combineReducers` 中的 reducers 保持相同的结构
 * 例如:
 *  reducers ={
 *      A:reducerA,
 *      B:reducerB,
 *      C:reducerC
 *    }
 *  preloadedState = {
 *      A:statA,
 *      B:stateB,
 *      C:stateC
 *    }
 *
 * @param {Function} enhancer The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */
export default function createStore(reducer, preloadedState, enhancer) {

  // 如果只传了两个参数,并且第二个参数为函数,第二个参数会被当作enhancer
  // 即 createStore(reducer, enhancer)
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }


  //*****  如果存在 enhancer 时,先使用 enhancer来生成代理的createStore方法,再使用代理的 createStore 来生成增强的 Store
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

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

  var currentReducer = reducer
  var currentState = preloadedState
  var currentListeners = []
  //定义一个数组用来存放listeners。就是一个函数数组,当state发生改变时,会循环执行这个数组里面的函数
  var nextListeners = currentListeners
  //nextListeners用来存储下一次的listeners数组。因为当state发生改变时,我们根据
  //currentListeners来循环执行函数,但是在这执行这些函数时,函数内部可能取消或者添加订阅
  //这时如果直接操作currentListeners ,就会造成错误

  // reducer函数是否正在执行的标识
  var isDispatching = false

  //以上这一大断都是用来对参数做一些判断处理,作用就像java中的重载的功能

  //确保nextListeners只是currentListeners的一个copy,这样确保当订阅和取消订阅时,不会影响到currentListeners
  //这个方法在订阅和取消订阅时都会被调用
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   * 从store中读取 state 树
   * @returns {any} The current state tree of your application.
   */
  function getState() {
    return currentState
  }

  /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * 添加一个变更监听,当任何一个action被分发时,它都会被回调,并且相对应的部分的 state 很可能被改变。
   * 你可以在监听中调用 getState() 方法来读取当前的 state 树
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   * 你可以在变更监听中调用 dispatch() 方法
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   * 1. 被订阅的内容是一个快照。当监听器被调用期间,如果你订阅/取消订阅,那么在当前进程中调用 dispatch() 将不会产生任何效果
   *  可是,下一个调用 dispatch() 时,无法dispatch()是否嵌套调用,都将使用一个当前快照订阅都列表
   *
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   * 2. 监听者并不希望看到所有的state改变,例如一个state在一次的dispatch()中可能被改变很多次。
   * 不管怎样,它都将保证所有的订阅者在调用 dispatch() 之前注册的监听都将会被调用,并能获取到最终的state
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }

    // 避免重复退订
    var isSubscribed = true

    ensureCanMutateNextListeners()

    //subscribe 只是把新注册的listener放到了nextListeners数组中,等待下一次dispatch时被调用
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      //从nextListeners中清除掉已退订的listener
      ensureCanMutateNextListeners()
      var index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   * 分发一个action,它是触发state改变的唯一方式
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   * reducer方法用于创store ,当 state树中指定的 action时,reducer方法将会被调用。
   * reducer方法的返回什将用于 创建 下一个state树的快照,并且listeners 会收到通知。
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   * action 只支持简单对象,如果你想分发一个 promise,Observable, thunk 或者其他什么,
   * 你需要将这些能力放到对应的中间件中(middleware),包装到你的store的创建方法中(createStore)
   *
   * @param {Object} action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns {Object} For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   *
   * 注意:如果你使用了一个middleware,它可能是会把dispatch() 方法包成其他的东西,例如一个promise
   *
   */
  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?'
      )
    }

    // 判断reducer是否正在执行,因为redux不允许在reducer中执行dispatch方法
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    //执行reducer方法,生成新的state
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    //一次dispatch执行完毕后,将 nextListeners 赋值组 currentListeners
    //然后通知所有的listener
    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }

    return action
  }

  //替换reducer,当你的app要对代码进行分割并且你想动态加载一些reducers的时候使用
  /**
   * Replaces the reducer currently used by the store to calculate the state.
   * 替换当前的reducer,用于 store计算state
   * You might need this if your app implements code splitting and you want to
   * load some of the reducers dynamically. You might also need this if you
   * implement a hot reloading mechanism for Redux.
   * 当你的app要对代码进行分割并且你想动态加载一些reducers的时候,你可能会需要用到这个方法。
   * 当你要给redux实施一个重载机制
   *
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    //重新执行一次初始化的dispatch,初始化state
    dispatch({ type: ActionTypes.INIT })
  }

  //这是留给 可观察/响应式库 的接口,例如 RxJS
  /**
   * Interoperability point for observable/reactive libraries.
   * 这是留给 可观察/响应式库 的接口
   * 如果您了解 RxJS 等响应式编程库,那可能会用到这个接口,否则请略过
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/zenparsing/es-observable
   */
  function observable() {
    var outerSubscribe = subscribe
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      subscribe(observer) {
        if (typeof observer !== 'object') {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        var unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  //当store创建好后,要执行一次初始化的dispatch,这样每一个reducer才会return他们的实始的state.
  //这样才能构建出一个完整的初始的state tree
  dispatch({ type: ActionTypes.INIT })


  //返回 store对象
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

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.
 * 从右到左组件一组方法,最右边的方法可以得到与生成的最终方法相同数量的argument
 *
 * @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) {
  //如果没有传入function,则返回一个空方法,该方法只是将接收到的参数直接返回
  if (funcs.length === 0) {
    return arg => arg
  }

  //如果只有一个方法,则直接返回该方法
  if (funcs.length === 1) {
    return funcs[0]
  }

  //取出最后一个方法作为初始化方法
  const last = funcs[funcs.length - 1]
  //取出除最后一个方法外的其余方法用于遍历
  const rest = funcs.slice(0, -1)
  //从右到左合并funcs方法
  //将方法的参数,作为最后一个方法的参数,这个方法的返回值,作为下一个方法的入参
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}


CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

combineReducers

import { ActionTypes } from './createStore'
import isPlainObject from 'lodash/isPlainObject'
import warning from './utils/warning'

// redux中要求reducer必须一定要有返回值,如果没有值回值,就会抛这个错误
function getUndefinedStateErrorMessage(key, action) {
  var actionType = action && action.type
  var actionName = actionType && `"${actionType.toString()}"` || 'an action'

  return (
    `Given action ${actionName}, reducer "${key}" returned undefined. ` +
    `To ignore an action, you must explicitly return the previous state.`
  )
}

//获取与预期不符的state的警告信息
//1、reducers是不是空的,是否存在有效的reducer
//2、state是否是简单对象
//3、state中是否存在未被reducer处理的部分
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
  var reducerKeys = Object.keys(reducers)
  var 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)) {
    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('", "')}"`
    )
  }

  var unexpectedKeys = Object.keys(inputState).filter(key =>
    !reducers.hasOwnProperty(key) &&
    !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })

  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.`
    )
  }
}


//校验所有子reducer的初始值和执行后结果是否为空,是则提示错误。
function assertReducerSanity(reducers) {
  //遍历reducer
  Object.keys(reducers).forEach(key => {
    var reducer = reducers[key]

    //获取实始值,并判断其是否为undefined
    var initialState = reducer(undefined, { type: ActionTypes.INIT })

    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.`
      )
    }

    //创建一个不存在的action,判断初始state其是否为undefined
    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
    if (typeof reducer(undefined, { type }) === 'undefined') {
      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.`
      )
    }
  })
}

/**
 * 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.
 * 将多个不同的reducer方法,转换成一个reducer方法。它将调用每一个子reducer,并将他们的返回值合并成一个state对象。
 * 每一个reducer的返回值将以 reducer的key做为在state上的key
 *
 * @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.
 * reducers是一组需要合并成一个reudcer的不同的reducer方法。一个获得它的简便方式就是使用ES6的 `import * as reducers` 语法
 * 无论是任何类型的action,这些reducers都不能返回undefined。
 * 如果通过这些方法的state是 undefined,那它们应该返回它们的初始state。任何未注删的action类型,返回当前的state
 *
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 * 返回一个reducer方法,
 * 并创建一个相同结构的state
 *
 */
export default function combineReducers(reducers) {
  //取出所有reducer的key,对reducer进行遍历,将reducer复制到一个finalReducers对象上,过滤了非函数属性
  var reducerKeys = Object.keys(reducers)

  var finalReducers = {}
  for (var i = 0; i < reducerKeys.length; i++) {
    var 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]
    }
  }
  var finalReducerKeys = Object.keys(finalReducers)

  if (process.env.NODE_ENV !== 'production') {
    var unexpectedKeyCache = {}
  }

  //验证reducer方法是否正确,是否有返回state
  var sanityError
  try {
    assertReducerSanity(finalReducers)
  } catch (e) {
    sanityError = e
  }


  /**
   * 返回一个合并后的reducer方法
   * 这个方法实际上就是遍历执行每一个reducer方法,再将每个redcuer的返回值合并成一个新的state
   */
  return function combination(state = {}, action) {
    if (sanityError) {
      throw sanityError
    }

    if (process.env.NODE_ENV !== 'production') {
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    // state是否改变的标识
    var hasChanged = false
    //新的state对象
    var nextState = {}
    for (var i = 0; i < finalReducerKeys.length; i++) {
      var key = finalReducerKeys[i]
      var reducer = finalReducers[key]            //取出reducer
      var previousStateForKey = state[key]        //取出对应的state
      //执行reducer方法,返回处理后的新的state
      var nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

bindActionCreators



//bindActionCreator 对actionCreator做了一层包装,将actionCreator返回的action,用 dispatch分发出去
function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

//bindActionCreator 的作用,是把每一个actionCreator和 dispatch绑定起来,
/**
 * 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.
 * 将一个由值为 action creator的对象进行转换,转换后每一个方法都将包装到 dispatch的调用中,这样它们才会被直接调用。
 * 这只是一个转换方法,你也可以自己通过手动写 store.dispatch(actionCreator.doSomething()) 来实现相同的作业。
 *
 * 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 是否个方法,则直接bindActionCreator
  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"?`
    )
  }

  //如果actionCreators 是key、value型式的一组actionCreator,则遍历每个 actionCreator 进行bindActionCreator,并返回结构相同,但经过绑定的actionCreator对象
  var keys = Object.keys(actionCreators)
  var boundActionCreators = {}
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    var actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

applyMiddleware

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.
 *
 *  创建一个 store 增强方法 ,这个方法使用了中间件来分发 Redux store。
 *  方便于不同种类的任务,例如 使用一种简洁的方式来表达异步动作 ,或者 记录每个动作的装载
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 * 可以阅读 `redux-thunk` 库,当作为 redux 中间件的一个例子
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 * 因为中间件可能是异步的,所以`redux-thunk`应该作为中间件链的第一个 store 增强器来使用
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 * 注意每一个中间件都会被注入 `dispatch` 和 `getState` 参数
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 *
 * 入参是一组 middlewares ,
 *
 * @returns {Function} A store enhancer applying the middleware.
 * 返回一个使用了中间件的增强方法

 *
 *
 */
export default function applyMiddleware(...middlewares) {

  /**
   *  return 一个 enhancer方法
   *
   *  enhancer方法接收一个参数 createStore
   *
   *  enhancer 方法执行后,返回一个createStore的代理方法,这个方法接收的参数与createStore方法是一致的
   *    reducer ,preloadedState ,enhancer
   *  所以 enhancer 是可以被不断的叠加
   */
  return (createStore) => (reducer, preloadedState, enhancer) => {

    // 调用createStore方法创建出一个store对象
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch

    // middleware 方法链数组,用来保存middleware初始化后的处理方法
    var chain = []

    // 每个 middleware 的参数都是 getState,dispatch 这两个方法,这是一种柯里化处理方法
    // middleware({getState,dispatch}) 返回的是  next => action =>{ next(action) } 的方法
    // next是dispatch方法或被前一层中间件包装过的dispatch代理方法
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    //相当于 dispatch = middlewareChain1(middlewareChain2(middlewareChainX...(middlewareChainLast(store.dispatch))))
    //返回经过所有中间件包装过的dispatch方法,替换原始的dispatch方法
    //当执行store.dispatch时,将依次处理各个中间间中所定义的事务,最后才执行原始的dispatch方法,也就是createStore中定义的dispatch方法
    return {
      ...store,
      dispatch
    }
  }
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

index.js

import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'

/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/
function isCrushed() {}

if (
  process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' &&
  isCrushed.name !== 'isCrushed'
) {
  warning(
    'You are currently using minified code outside of NODE_ENV === \'production\'. ' +
    'This means that you are running a slower development build of Redux. ' +
    'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
    'or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' +
    'to ensure you have the correct code for your production build.'
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

warning

/**
 * Prints a warning in the console if it exists.
 * 在控制台中打印警告
 *   —— 做了一个兼容处理,可能是为了兼容RN
 *
 * @param {String} message The warning message.
 * @returns {void}
 */
export default function warning(message) {
  /* eslint-disable no-console */
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    console.error(message)
  }
  /* eslint-enable no-console */
  try {
    // This error was thrown as a convenience so that if you enable
    // "break on all exceptions" in your console,
    // it would pause the execution at this line.
    throw new Error(message)
  /* eslint-disable no-empty */
  } catch (e) { }
  /* eslint-enable no-empty */
}

CodingMeUp avatar Nov 17 '17 04:11 CodingMeUp

第 24 题:聊聊 Redux 和 Vuex 的设计思想

Vuex、Flux、Redux、Redux-saga、Dva、MobX 比较,不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。什么是共享状态?比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态

Redux: view——>actions——>reducer——>state变化——>view变化(同步异步一样)

Vuex: view——>commit——>mutations——>state变化——>view变化(同步操作) view——>dispatch——>actions——>mutations——>state变化——>view变化(异步操作)

CodingMeUp avatar Jun 22 '20 16:06 CodingMeUp