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 3.6.0 源码阅读 之 src/createStore.js #6

Open
loveky opened this issue Dec 1, 2016 · 0 comments
Open

Redux 3.6.0 源码阅读 之 src/createStore.js #6

loveky opened this issue Dec 1, 2016 · 0 comments

Comments

@loveky
Copy link
Owner

loveky commented Dec 1, 2016

嗯,就是学习一下,读读源码。顺便把源码里的文档翻译了一下并添加了一些简单的注释。

本文对应的文件是src/createStore.js

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

/**
 * ActionTypes里定义的是Redux保留的私有action。
 * 对于任何未知的action,你必须返回store的当前状态。
 * 如果传入的当前状态是undefined,你必须返回store的初始状态。
 * 不要在应用代码中直接引用这些action。
 */
export var ActionTypes = {
  INIT: '@@redux/INIT'
}

/**
 * createStore方法用于创建一个保存程序状态的store。
 * 改变store中数据的唯一方法是调用store的`dispatch()`方法。
 *
 * 你的应用中应该只有一个store。为了将程序状态中不同部分的变更逻辑
 * 组合在一起,你可以通过`combineReducers`方法将多个reducer组合成一个reducer。
 *
 * @param {Function} reducer 一个返回应用下一状态的函数,入参是程序的当前状态以及
 * 要发送的action。
 *
 * @param {any} [preloadedState] store的初始状态。你可以选择性的为store指定一个
 * 初始状态。
 * 如果你使用了`combineReducers`方法来生成最终的reducer。那么这个初始状态对象的
 * 结构必须与调用`combineReducers`方法时传入的参数的结构保持对应关系。
 *
 * @param {Function} enhancer store增强器。你可以选择性的传入一个增强函数来扩展
 * store的功能,例如中间件,时间旅行,持久化等。Redux自带的唯一一个增强器是
 * `applyMiddleware()`方法。
 *
 * @returns {Store} 返回一个可以读取状态,发送action以及订阅变更通知的Redux store。
 */
export default function createStore(reducer, preloadedState, enhancer) {
  // 如果只传入reducer和enhancer,则store的初始状态为undefined
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  // enhancer必须是一个函数
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    // 返回使用enhancer增强后的store
    return enhancer(createStore)(reducer, preloadedState)
  }


  // reducer必须是一个函数
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  var currentReducer = reducer
  var currentState = preloadedState
  var currentListeners = []
  var nextListeners = currentListeners
  var isDispatching = false

  // 在每次修改监听函数数组之前复制一份,实际的修改发生在这个新
  // 复制出来的数组上。确保在某次dispatch发生前就存在的监听器,
  // 在该次dispatch之后都能被触发一次。
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * 读取store状态
   *
   * @returns {any} 返回store的当前状态
   */
  function getState() {
    return currentState
  }

  /**
   *
   * 新增一个变更监听函数。每当dispatch了一个action之后监听函数都会被触发一次。
   * 你可以在监听函数中通过`getState()`方法获取store的最新状态。
   *
   * 你可以在一个回调函数中再次调用`dispatch()`。但需要注意以下两点:
   *
   * 1. 在每一次dispatch()调用执行之前,监听函数数组都会被复制一份(通过前文提到
   * 的ensureCanMutateNextListeners方法)。如果你在监听函数中增加或删除其他监听函
   * 数,那么这些操作并不会影响到当前进行中的这一轮dispatch。而下一次dispatch,
   * 不论其是否是嵌套调用,都会使用最新的,修改后的监听函数列表。
   *
   * 2. 由于在一个监听函数执行前可能已经进行了多次嵌套的dispatch调用,因此不能保证
   * 每个监听函数都可以获取到所有的状态变更。然而,可以确定的是,在某次dispatch
   * 触发之前已经注册的监听函数都可以读取到这次diapatch之后store的最新状态。
   *
   * @param {Function} listener 每次dispatch之后执行的回调函数
   * @returns {Function} 返回一个用于取消这次订阅的函数
   */
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }

    var isSubscribed = true

    // 添加监听函数前确保只操作当前数组的一份拷贝
    ensureCanMutateNextListeners()
    nextListeners.push(listener)

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

      isSubscribed = false

      // 移除监听函数前确保只操作当前数组的一份拷贝
      ensureCanMutateNextListeners()
      var index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  /**
   * 发送一个action。这是触发状态变更的唯一方法。
   *
   * 每次发送action的时候,用于创建store的`reducer`函数都会被调用一次。调用时
   * 传入的参数是当前的状态以及被发送的`action`。调用的返回值会被当做更新后的
   * 状态。调用完成后,所有的状态监听函数都会被触发。
   *
   * 基础实现中仅支持发送形式为简单对象的action。如果你希望可以发送Promise,Observable,
   * thunk或是其它形式的action,你需要使用相应的中间件把store创建函数封装起来。
   * 你可以在`redux-thunk`模块的文档中找到这方面的示例。不过即使在这些中间件内部
   * 还是通过dispatch方法发送了简单对象形式的action。
   *
   * @param {Object} action 一个表示变更内容的对象。保证你的action是可被序列化的
   * 是一种很好的实践,这样你就可以记录并回放用户的操作,或是使用可以穿梭时间
   * 的`redux-devtools`插件。一个action必须有一个值不为`undefined`的`type`属性。
   * 推荐你使用字符串常量来表示action类型。
   *
   * @returns {Object} 为了方便起见,返回你传入的action对象。
   *
   * 要注意的是,如果你使用了一个自定义的中间件,它可能会把`dispatch()`的返回值
   * 封装成其它内容(比如,一个你可以await的Promise)
   */
  function dispatch(action) {
    // 如果action不是简单对象,抛出异常
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
        'Use custom middleware for async actions.'
      )
    }

    // 如果action的类型是undefined,抛出异常
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?'
      )
    }

    // reducer内部不允许再次调用dispatch,否则抛出异常
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    // 执行reducer,传入当前状态和action
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    // 触发所有的状态监听回调函数
    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }

    return action
  }

  /**
   * 替换store当前使用的reducer函数
   * 
   * 如果你的应用程序实现了代码拆分并且你希望动态加载某些reducer的时候你
   * 可能会用到这个方法。或者当你要为Redux实现一个热加载机制的时候,你也
   * 会用到它。
   *
   * @param {Function} nextReducer 要使用的新reducer
   *
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
  }

  /**
   * 为observable/reactive库预留的交互接口。
   * @returns {observable} 表示状态变更的最简单的observable对象
   * 想要获取更多信息,可以查看observable提案:
   * https://github.com/zenparsing/es-observable
   */
  function observable() {
    // 首先保留对Redux中subscribe方法的引用,在observable的世界里
    // 有一个同名方法。
    var outerSubscribe = subscribe
    return {
      /**
       * 一个极简的observable订阅方法。
       * @param {Object} observer 任何可以作为observer使用的对象
       * observer对象应该包含一个`next`方法。
       * @returns {subscription} 返回一个带有`unsbscribe`方法的对象。该
       * 方法将用于停止接收来自store的状态变更信息。
       */
      subscribe(observer) {
        // observer参数必须是一个对象,否则抛出异常。但并未检测是否有next方法。。。
        if (typeof observer !== 'object') {
          throw new TypeError('Expected the observer to be an object.')
        }

        // 创建一个状态变更回调函数。逻辑很简单,把store最新的状态传给observer
        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        // 立即执行一次回调函数,把当前状态传给observer
        observeState()
        var unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },
      // 根据observable提案,[Symbol.observable]()返回observable对象自身
      [$$observable]() {
        return this
      }
    }
  }

  // store创建好以后,立即发送一个初始化action。这样做是为了让reducer
  // 返回store的初始状态(当给reducer传入的当前state为undefined时,reducer
  // 会返回store的初始状态)。

  dispatch({ type: ActionTypes.INIT })

  // 返回创建好的store对象
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
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