深入浅出—Redux源码

目录

  • 一、维护一个状态树、发布订阅
    • 1.1 dispatch
    • 1.2 subscribe
    • 1.3 getState
    • 1.4 replaceReducer
    • 1.5 [$$observable]: observable
  • 二、中间件
  • 三、工具函数
    • 3.1 compose
    • 3.2 combineReducers
    • 3.3 bindActionCreator
  • 附录

阅读本文的前提条件,了解 Redux基本概念。
杂谈:
  了解一个框架,首先得明白, 框架解决的痛点是什么
  前端需要对状态进行管理(同步状态、 异步状态),讲实话,随便写写代码都是状态管理,但是这是我们的追求么?当然不是,我们需要优雅地对前端状态进行管理。在MVC架构中,可以简单映射为数据(Model)----界面(View)----交互操作(Controller)。
  所谓的状态管理基本就是对数据和交互操作的管理。Redux基本就做了这样一件事情。
总结:
Redux源码基本做了两个操作:
1)维护一个状态树
2)状态的发布订阅

一、维护一个状态树、发布订阅

/** 创建状态树
*  @param {Function} reducer:reducer就是你写的函数,actionType---->new state
*  @param {any} preloadedState: store的初始状态,可以是任何形式,可以是函数,因为有的初始状态可能需要从服务端获取数据进行merge
*  @param {Function} enhancer: 中间件,在action发出之前经历的一系列骚操作
*  @returns {Store} :返回值是一个对象,可以对Store进行一系列操作,订阅、获取等
*/
export default function createStore(reducer, preloadedState, enhancer) {
	 // 在源码中,将preloadedState和enhancer可以看作是同一个作用的,就是在生成初始状态之前进行的一系列操作
	 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
  	}
  	/**
  	* 如果定义了中间件,要到中间件中去执行相应的操作,传入的这几个参数有什么用?待会在中间件中解释
  	*  @params {createStore}  
  	*  @params {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)
	 }
	 /**
	 * 如果没有中间件,那么我们就继续执行,创建Store
	 *  @variable 
	 * 		-currentReducer:当前的reducer,也就是你传入的reducer
	 * 		-currentState:当前的状态,你传入的state的初始值,如果没有传入,undefiend
	 * 		-currentListener:既然是发布订阅,listener是用来监听的
	 * 		-nextListener:下一个监听的
	 * 		-isDispatching:用来判断是否生成新的状态,默认在生成状态的时候,不允许获取状态,相当于加了一个锁,理由嘛,很简单,防止获取旧状态
	 */
	  let currentReducer = reducer
	  let currentState = preloadedState
	  let currentListeners = []
	  let nextListeners = currentListeners
	  let isDispatching = false
	/**
	* 接下来就是把对store的所有操作作为一个对象返回
	*/
	  return {
	    dispatch,
	    subscribe,
	    getState,
	    replaceReducer,
	    [$$observable]: observable
	  }
}

1.1 dispatch

/**
   * Dispatches an action. 是唯一改变store的方法
   *
   * @param {Object} action 必须是一个对象,plainObject
   *
   * @returns {Object} 返回值和你输入的一样,没有也是一样的
   *
   * 值得注意的是, 如果你是自定义的中间件, 需要把`dispatch()`包装下,返回其他的东西,最终都是返回一个plainObject
   * 
   * 具体的逻辑,大家自己看吧,本来加了一些注释,但是发现不加注释可读性更好
   */
  function dispatch(action) {
  
    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()
    }

    return action
  }

1.2 subscribe

/**
   * 其实就做了一件事情,把回调函数callback放进数组里面去
   * @param {Function} 当dispatch的时候的回调函数
   * @returns {Function} 返回这个取消订阅的函数
   */
  function subscribe(listener) {

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

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

1.3 getState

/** 
   * @returns {any}  简单得不能再简单的一个操作
   */
  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
  }

1.4 replaceReducer

/**
   * @param {Function} 动态加载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.REPLACE })
  }

1.5 [$$observable]: observable

/**引用了observabla的库,这是一个提案,为了以后的兼容性,如果没有这个Redux一样可以运行,有兴趣的可以了解
   * [Observable提案](https://github.com/tc39/proposal-observable)
   * [Observable说明](https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/)
   * subscribe : 是前面的监听回调函数
   */
  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
      }
    }
  }

二、中间件

/**
 * redux中间件的源码很简洁,就是
 * @param {...Function} middlewares 一系列中间件
 * @returns {Function} 返回值是enchancer去执行中间件
 * 参考前面中间件执行的时候,把reducer和state穿进去,然后中间件进行其他操作
 *     	return enhancer(createStore)(reducer, preloadedState)
 */
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
    }
  }
}

三、工具函数

上面的redux基本原理的源码,基本就结束了(是不是太简单了),除了上面之外,还提供了几个工具函数compose、combineReducers、bindActionCreators三个工具函数

3.1 compose

  compose,主要用在组合中间件的时候,将中间件组合在一起,毕竟只能接受一个函数,而且要求函数按照顺序一个一个执行

/**
* compose就干了一件事情,把函数按照先后顺序调用
* compose (f, g, h)===>  (...args) => f(f(h(...args)))
* reduce就是按顺序执行的函数,最常用的就是求和
*/
function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

3.2 combineReducers

combineReducers,主要用来将多个reducers组合为一个reducers,写过redux的一般会写Containers,每个containers都有reducers,那么怎么将这些reducers组合在一起,combineReducers做的就是这件事。毕竟Redux要求只有一个Store只有一个Reducers。

/**
 * @param {Object} 输入参数为一个对象,对象的值为各个reducers
 *
 * @returns {Function} 返回值就是一个将所有reducers包裹在一起的函数
 * 新建了一个finalReducers对象,对象中  键为reducers的键,值为各个reducers(感觉有点废话,但是确实是这样的)
 */
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 (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }
  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)
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

3.3 bindActionCreator

bindActionCreator的作用是为了隐藏dispatch,将dispatch变成一个可执行函数。
核心就是bindActionCreators

function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

但是可能有很多actions,所以就需要便利操作,为每一个action隐藏dispatch

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  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
}

附录

如何判断一个对象是PlainObject,自行理解吧,是在写不动了,再不行,就翻翻课本的原型链。

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
}

Redux git仓库

你可能感兴趣的:(Redux,Javascript,深入浅出框架源码,深入浅出前端技术)