redux源码阅读

redux安装

初始化项目

npm init
复制代码

下载redux

npm install redux --save
复制代码

代码目录

redux的目录结构很简单,如下图:

源码阅读

createStore.js


/**
 * 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`.
 *
 * @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.
 *
 * @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) {
    // 对createStore的选填参数进行判断
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
   // enhancer和preloadedState进行互换
    enhancer = preloadedState
    preloadedState = undefined
  }
    //  如果传参enhancer,那么enhancer必须是方法
    //  通过enhancer把createStore包装一次,并传入reducer,preloadedState,
    //  这是传了中间件的情况
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    return enhancer(createStore)(reducer, preloadedState)
  }
    // 验证reducer
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  let currentReducer = reducer // 当前的reducer函数
  let currentState = preloadedState // 当前状态树
  let currentListeners = [] // 当前监听列表
  let nextListeners = currentListeners // 当前监听列表的引用
  let isDispatching = false // 是否正在分发
    // 如果nextListeners和currentListeners的引用地址相同,则nextListeners拷贝一份currentListeners
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   *
   * @returns {any} The current state tree of your application.
   * 
   */ 
   //当isDispatching为false时,返回当前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
  }
  // getState 方法,返回当前状态树
    /**
   * 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.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 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.
   *
   * 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.
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */
   // 组册一个监听函数
  function subscribe(listener) {
  // 参数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.'
      )
    }
   // 设置私有变量isSubscribed为true
    let isSubscribed = true
    ensureCanMutateNextListeners()
    // 将新的listener添加到nextListeners里
    nextListeners.push(listener)
   // 返回取消监听方法unsubscribe,通过判断判断当前监听是否存在等可注销当前监听。
    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)
    }
  }
  
  function dispatch(action) {
    // 通过isPlainObject判断是否为对象
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // 判断action的type是否存在,不存在抛出错误
    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.')
    }
    // 通过reducer返回一个新的state座位当前的state
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    // 然后调用每一个监听器分发内容
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // 最后返回action
    return action
  }
    // nextReducer必须是方法,重新初始化状态树
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }
  
  // 用于观察者模式
  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
  }
}
复制代码

createStore接受三个参数 :

reducer、preloadedState、enhancer

其中reducer是必传后面两个是选传
复制代码

reducer:这是一个函数,接受两个参数(stsate和action),reducer在dispatch中被调用。

preloadedState: 初始的state。

enhancer: enhancer也是一个函数,是一个增强的createStore,是使用applyMiddleware来生成的。

createStore的内部环境及返回值 :

createStore方法形成了一个闭包其中包含了几个重要的数据:

reducer、currentState(简称state)、currentListeners、nextListeners(简称listeners,其元素简称listener)
复制代码

返回的是一个对象,这个对象中包含四个方法:

dispatch、subscribe、getState、replaceReducer
复制代码

下面我们分别对这四个方法进行分析

dispatch:redux中唯一改变state树的方法,它可以分发一个action然后通过reducer改变state树,同时需要一个reducer执行结束才能进行下一次reducer,最后执行一次listener,因此每次state发生变化都会被监听到。

subscribe:用于订阅监听,为listeners增减就是通过它,最后返回销毁该listener的方法。

getState:获取当前state树。

replaceReducer: 替换当前store中的reducer,并初始化。

createStore通过闭包在内部创建了state状态树实现持久存储,然后通过暴露相应的方法实现对state的增删改查 compose.js

export default function compose(...funcs) {
// 传入的参数是一个数组,数组长度为0时返回一个 arg => arg的函数
  if (funcs.length === 0) {
    return arg => arg
  }
// 当数组为一时返回这个数组的第一个元素
  if (funcs.length === 1) {
    return funcs[0]
  }
// 当数组为大于1的数时,返回一个从左至有依次嵌套的的函数,内层的函数的返回值作为外层函数的参数
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

复制代码

reduce方法在平时也会有使用,在平时经常使用reduce对数组求和,比如:

[1,2,3,4].reduce((a,b)=>a+b)
复制代码

在这里的用法:

[funOne,funTwo,funThree].reduce((a, b) => (...args) => a(b(...args)))
//相当于 function(...args) { return funOne(funTwo(funThree(...args)))}
复制代码

从左至右依次嵌套,这样就可以理解compose的作用了

applyMiddleware.js

export default function applyMiddleware(...middlewares) {
// 返回一个函数并将createStore作为参数然后返回一个新的函数,新的函数再将之前的reducer和preloadedState作为参数传入
  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.`
      )
    }
    // 建立新的变量middlewareAPI
    const middlewareAPI = {
      // getStates属性
      getState: store.getState,
      // dispatch属性
      dispatch: (...args) => dispatch(...args)
    }
    // 传入middlewareAPI并执行每一个中间件,返回一个数组
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 重写dispatch
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}
复制代码

applyMiddleware就是用于创建createStore中enhancer函数的。

bindActionCreator.js

// bindActionCreator接受两个参数actionCreator,dispatch,
// 将action与dispatch结合并传入
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
// 如果action是以函数形式传进来的,那么就调用bindActionCreator方法
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
// actionCreators必须为对象类型
  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所有的属性名存入keys中
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  //  给actionCreators的每一个成员都绑定dispatch方法生成新的方法,
  // 然后注入新的对象中,新方法对应的key即为原来在actionCreators的名字
  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
}

复制代码

bindActionCreators的作用是将action与dispatch绑定,这里面又分为两种情况,如果 bindActionCreators的第一个参数是函数的,则直接绑定。如果第一个参数是对象则将其对象中的每一个action方法绑定。

combineReducers.js

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'

// 通过key和action生成错误信息
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)) {
    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('", "')}"`
    )
  }

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

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

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


// 检测reducer是否符合redux的规定
function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
    const 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. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      )
    }

    const 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, but can be null.`
      )
    }
  })
}
/**
 * 
 * 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) { 
  // 获取所有reducer的按键名
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  // 过滤reducer
  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]
    }
  }
  // finalReducers保存最后过滤过后的reducer集合
  // 存储过滤后的reducer的键名
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }
  // 检测每个reducer是否是符合标准的reducer
  let shapeAssertionError
  try {
    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
    // 下一个state树
    const nextState = {}
    // 每发起一次dispatch都会遍历所有的reduce
    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)
      // reducer返回的数据是undefined抛出错误
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 将相应的值赋给nextState相应的属性
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //如果当前action对应的reducer方法执行完后,该处数据没有变化,则返回原来的流程树
    return hasChanged ? nextState : state
  }
}
复制代码

combineReducers接受一个参数:

属性为reducer的对象
复制代码

combineReducers方法从字面就能理解,它的作用是将所有的reduce合并,通过每一次发起dispatch改变状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值