美文网首页
redux源码学习之createStore

redux源码学习之createStore

作者: hg2018 | 来源:发表于2019-01-14 19:37 被阅读0次

createStore中用到的主要知识点有:
1.原型及原型链
2.闭包
3.高阶函数
4.其中action派发动作的监听类似于订阅发布模式

概述
 * 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.
 *
 * 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`.

createStore函数是用来创建一个保存有整个状态树的store。要想改变store中的数据,唯一方式是执行其中的dispatch方法 。并且在你的应用程序中只能有一个单独的 store。为了指定状态树的不同部分如何响应操作,你可以通过combineReducers函数将几个reducer函数合并到一个单独的reducer函数。

本文将主要介绍下面两个方法:
dispatch-派发一个action,它是改变状态树的唯一途径。
subscribe-监听状态树的变化,任何时候派发一个action,都将调用当前的所有监听回调函数。

dispatch函数

if (!isPlainObject(action)) {
  throw new Error(
    'Actions must be plain objects. ' +
    'Use custom middleware for async actions.'
  )
}
/**
 * @param {any} obj The object to inspect.
 * @returns {boolean} True if the argument appears to be a plain object.
 */
export default function isPlainObject(obj) {
  if (typeof obj !== 'object' || obj === null) return false

  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }

  return Object.getPrototypeOf(obj) === proto
}

isPlainObject判断action是不是一个简单对象,即原型链只有一层,Object.getPrototype(action) === Object.prototype,如对象字面量创建的对象。而构造函数或class创建的对象就不符合要求。

if (typeof action.type === 'undefined') {
    throw new Error(
      'Actions may not have an undefined "type" property. ' +
      'Have you misspelled a constant?'
    )
 }

action的type属性不能为undefined。

if (isDispatching) {
  throw new Error('Reducers may not dispatch actions.')
}

不能同时派发多个action。

try {
  isDispatching = true
  currentState = currentReducer(currentState, action)
} finally {
  isDispatching = false
}

执行当前reducer函数获取新的state。

const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
  const listener = listeners[i]
  listener()
}

执行所有监听回调函数。

subscribe函数

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

传给subscribe参数的必须是function,且在派发action时不允许调用该方法。

let isSubscribed = true

ensureCanMutateNextListeners()
nextListeners.push(listener)

function ensureCanMutateNextListeners() {
  if (nextListeners === currentListeners) {
    nextListeners = currentListeners.slice()
  }
}

其中isSubscribed因为闭包的原因,在subscribe执行后返回的函数中可以获取该变量,并确保后面的删除操作只执行一次。ensureCanMutateNextListeners函数确保nextListeners和currentListeners指向不同的对象。

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

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

subscribe返回的是一个函数,用来删除当前传入的监听函数。将isSubscribed赋值为false用来确保只执行一次。

介绍完dispatch和subscribe,现在来看一下createStore整体执行流程。

if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
      'createStore(). This is not supported. Instead, compose them ' +
      'together to a single function'
    )
}

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

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

reducer必须为函数,否则抛异常。当enhancer有值时,会用到函数柯理化求值。

相关文章

网友评论

      本文标题:redux源码学习之createStore

      本文链接:https://www.haomeiwen.com/subject/vsgjdqtx.html