Redux源码完全解读

注:redux核心代码位于src目录下,因此以目录结构来分析,文中仅粘贴部分核心代码。

src

index.js
import createStore from './createStore'
// 省略导入
......

// 定义一个空函数
function isCrushed() {}
// 代码压缩后,会对变量进行重命名,也就是通过这种方式,减少字节,判断函数名是否发生变化
// 来确定是否使用了压缩后的版本。
if (
  process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' &&
  isCrushed.name !== 'isCrushed'
) {
  // 判断函数的运行环境,在非生产环境下运行时,isCrushed函数名不是'isCrushed',则说明
  // 自视运行的是压缩后代码,对开发者提出警告⚠️开发环境下使用压缩文件,会增加额外的构建时间。
  warning(
    'You are currently using minified code outside of NODE_ENV === "production"......' 
  )
}

// 输出redux的核心api
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}
复制代码

createStore
import $$observable from 'symbol-observable'

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

/**
 * @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.
 */
// createStore构建一个全局唯一的store用于存储全局状态。store是一个树?结构对象,可读并在一定情况下
// 可写,当然你也可以直接对sotre对象进行改写,那么产生的出乎意料的bug也会让你惊喜,redux本身并没有对
// store的写操作进行限制,全凭使用者的自觉。ps:为了不出bug,不要直接改写store数据。
export default function createStore(reducer, preloadedState, enhancer) {
  // createStore接收最多三个参数
  // @reducer<:Function> 一个纯函数衍生器,用于从旧的状态,衍生新的状态,并且这个新状态是全新的拷贝。
  // preloadedState<:Object> 初始化状态,用于从一个基础状态构建一个初始store。
  // enhance<:Function> reducer的增强器,可以调用比如中间件函数等,来增强redux功能。
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    // enhancer可能是第二个参数,此时需要纠正各参数值。曾考虑为什么不将enhancer和preloadedState参数
    // 调换位置,如此便不需要这一步处理,后想到大多数情况下我们可能不会使用enhancer,所以在参数设计时,尽量
    // 将次要参数的位置向后,或通过对象{key:value}的形式传入。
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      // enhancer必须为一个function
      throw new Error('Expected the enhancer to be a function.')
    }
    // 如果传入了enhancer,则使用enhaner增强createReducer函数,最后生成store
    return enhancer(createStore)(reducer, preloadedState)
  }

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

  // 预定义一系列可用变量
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function ensureCanMutateNextListeners() {
    // 注册事件存储在栈中,当前后两次栈相同时,进行一次拷贝,因为通常操作的nextListeners,这是
    // 最新的事件栈。防止在操作nextListeners时进行了写操作,导致修改了currentListeners。
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * @returns {any} The current state tree of your application.
   */
  function getState() {
    // getState方法很简单,直接返回当前的store,即返回currentState。获取store时,可能
    // 正处于某个action在dispatch的状态,那么此时的state,我们必然可以从组件内获取,不需要
    // 通过api来获取。
    // 假设:如果在dispatch时获取state,这种行为往往和我们预期不太相同,因为我们通常想要获取的
    // 是最新状态的state,而此时的store状态即将被更新。
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing...... ' 
      )
    }

    return currentState
  }

  /**
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */
  function subscribe(listener) {
    // redux支持订阅store状态的变化,可向subscribe传入一个注册函数。
    if (typeof listener !== 'function') {
      // 类型校验
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      // 禁止在dispatch过程中进行订阅。
      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.'
      )
    }

    let isSubscribed = true
		// 确保可以安全操作nextListeners
    ensureCanMutateNextListeners()
    // 将订阅函数推入订阅栈
    nextListeners.push(listener)
    // 订阅完成后,返回一个取消订阅的方法
    return function unsubscribe() {
      if (!isSubscribed) {
        // 防止反复取消订阅,后面使用了splice方法,保证不会取订错误的函数
        return
      }

      if (isDispatching) {
        // dispatch时禁止取消订阅
        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)
    }
  }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * 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.
   *
   * 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.
   *
   * @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).
   */
  // dispatch是唯一可以改变store的渠道,action要求只能是简单对象,并包含一个type属性
  function dispatch(action) {
    if (!isPlainObject(action)) {
      // action需要是一个简单函数,可以是对象字面量 a = {} 或 Object.create(null)
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      // 要求action必须包含type属性
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      // 不可以同时进行多个dispatch,避免同时修改store树?,导致状态紊乱
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      // 进入dispatching状态,此时会禁止一些特殊操作,如上面的subscribe, unsubscribe, getState
      // 传入reducer当前currentState和action生成新的store
      currentState = currentReducer(currentState, action)
    } finally {
      // store更新完毕后,修改dispatching状态
      isDispatching = false
    }
    // dispatch后执行订阅的回调函数,到了兑现约定的时候了,需要通知订阅函数,store发生变化了。
    // 并把listerners进行更新
    // 思考:其实可以在listerner复制这里进行拷贝,便不需要ensureCanMutateNextListeners函数了
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      // 问题:为什么不把新的store传给回调函数???通过getstate吗?
      // 答案:想了一下,传递store给监听函数,有可能我们并不需要使用store,那么传递后还需要一个额外
      //			的指针内存。可以在需要时,通过getState获取最新的store。
      listener()
    }
		// 返回action
    return action
  }

  /**
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    // 作用如名字,替换reducer。如store一版,reducer也只需要一个。但在代码中,为了优化,可能
    // 会做一些代码分割的操作。可能会根据模块分割store,分割reducer。但最终还是需要将reducer
    // 组装在一起的。
    if (typeof nextReducer !== 'function') {
      // 参数类型检验,nextReducer必须是函数
      throw new Error('Expected the nextReducer to be a function.')
    }
    // 替换reducer,触发一个全局action, 通知reducer发生了替换。
    // 问题:为什么此处不做isDispatching的状态限制?
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  /**
   * Interoperability point for observable/reactive libraries.
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/tc39/proposal-observable
   */
  function observable() {
    const 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' || 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
      }
    }
  }

  // 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.
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

复制代码

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.
 *
 * @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) {
  // compose函数,可将多个函数参数组成合并成一个函数,参数从右自左执行。
  if (funcs.length === 0) {
    return arg => arg
  }
  // 思考: 需要加一步对参数类型的校验,否则会引发错误。
	// if (funcs.some((func) => !func instanceof Function)) {
  //	throw new Error('所有参数需要是函数类型') 
  // }
  if (funcs.length === 1) {
    return funcs[0]
  }
	// reduce遍历整个参数序列,时间复杂度On(n)。合并成的对象参数将取自最后一个函数的参数。
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

复制代码

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

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

// 此方法用于检查store和reduces的key是否完全匹配,不匹配则发出提示
function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  // 获取传入reducers的序列key。
  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) {
    // combineReducers接收的reducers参数必须是一个对象,内容为key:value序列。
    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)) {
    // 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('", "')}"`
    )
  }
	// state和reducers必须key匹配,unexpectedKeys为inputState中不与reducers匹配的key
  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )
	// 将不匹配的key传送给unexpectedKeyCache参数
  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })
	// 如果在发生replaceReducer,则退出这段程序
  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是否做了默认处理,在任何情况下都需要衍生一个新的state或返回currentState或者null
function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
    const initialState = reducer(undefined, { type: ActionTypes.INIT })

    // 测试reducer功能边界条件处理是否健全
    if (typeof initialState === 'undefined') {
      // 保证传入的reducers中总有一个处理通用type的case,即我们常写的reducer函数switch语句中
      // 对于default情况的处理,通常返回上一个状态的store。可以返回null但是不能返回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.`
      )
    }

    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      // 通过一个随机的验错type❌,来检查reducer是否对默认情况进行了处理。对于任何未知的type,
      // reducer都需要有一个合格的返回值。并且警告⚠️不能够对redux中内置的几个type做处理。
      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.`
      )
    }
  })
}

/**
 * @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.
 */
// 合并reducers的核心方法,返回一个全新的reducer。
export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  // 遍历reducers
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        // 在非生产环境下,reducers的每一个key都需要有一个子reducer作为值,不能为undefined
        warning(`No reducer provided for key "${key}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
      // 仅在reducers的各个属性值为函数时进行复制
      finalReducers[key] = reducers[key]
    }
  }
  // finalReducers的获取也可以这么写
  // const finalReducers = reducers.reduce((res, reducer, key) => {
  //   if (reducer instanceof Function) {
  //     res[key] = reducer;
	//   } else {
  //     if (process.env.NODE_ENV !== 'production') {
	//      warning(`No reducer provided for key "${key}"`)
	//     }
	//   }
  //   return res;
	// }, {})
  const finalReducerKeys = Object.keys(finalReducers)

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

  let shapeAssertionError
  try {
    // 检验reducer是否合规
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }
	// 最终返回的合并后的reducer
  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      // 如果reducer不合规,抛出错误。?
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
      // 非生产环境下,检查state和reduces的key是否完全匹配
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        // 如果检验不合规,进行提示
        warning(warningMessage)
      }
    }
		// 前后状态是否发生变化
    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]
      // 使用reducer来衍生新状态
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        // 如果某个action中没对所有的边界条件进行处理,抛出错误。
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      // 判断前后状态是否发生了变化,只要有部分状态发生变化,则store发生了变化
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // store发生变化,返回新的状态,否则返回旧状态
    return hasChanged ? nextState : state
  }
}

复制代码

bindActionCreators
function bindActionCreator(actionCreator, dispatch) {
  // 核心方法,为action生成器绑定dispatch,不需要每次从store.dispatch获取,当然这种方式也可以。
  return function() {
    // 为actioncreater绑定运行时时上下文环境,确保其执行时this指向其所属环境
    return dispatch(actionCreator.apply(this, arguments))
  }
}

/**
 * @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) {
  if (typeof actionCreators === 'function') {
    // 如果直接传入了一个函数,则直接进行绑定
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== 'object' || actionCreators === null) {
    // actionCreators类型校验,必须为对象类型或函数。
    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"?`
    )
  }

  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') {
      // 检验actionCreators中每个actionCreator是否为函数
      // 仅为actionCreator是函数的部分绑定dispatch
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  // 返回绑定dispatch后的actionCreators 
  return boundActionCreators
}

复制代码

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.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
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)
    }
    // 遍历调用middware增强dispatch方法,中间件可以链式合并。
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

复制代码

utils

actionTypes
/**
 * 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.
 */

const randomString = () =>
  Math.random()
    .toString(36)
    .substring(7)
    .split('')
    .join('.')
// redux内置的action type, 用于在store初始化,replaceReducer,注入代码时通知或校验
const ActionTypes = {
  INIT: `@@redux/INIT${randomString()}`,
  REPLACE: `@@redux/REPLACE${randomString()}`,
  PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}

export default ActionTypes

复制代码

isPlainObject
/**
 * @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
    proto = Object.getPrototypeOf(proto)
  }
  // 判断obj是不是对象字面量或Object构建的实例对象
  return Object.getPrototypeOf(obj) === proto
}

复制代码

warning
/**
 * Prints a warning in the console if it exists.
 *
 * @param {String} message The warning message.
 * @returns {void}
 */
export default function warning(message) {
  /* eslint-disable no-console */
  // 判断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)
  } catch (e) {} // eslint-disable-line no-empty
}
复制代码

Redux阅读体验

学习到

  1. Object.keys(someobj).map((v, k) => someobj[k])

    可以替代for...of...
    支持es6可以使用Objec.entries

  2. compose方法的reduce实现,reduce真的很强!

(...args) => (..params) => args.reduce((res, curr) => res(curr(..params)))
// reduce便捷在于返回的无限制和累积效应
// 无论是map, foreach, for...in...都无法达到这么优秀的品质
复制代码
  1. let lock = true;/* somewhere */ lock = false;

    想不到合适名字,就叫运行锁,开发中这种模式很常用,一个bool类型的变量,便可以区别两种状态,就像寄存器的两个状态。

  2. 接口设计

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}
复制代码
只暴露尽可能少且必要的接口,接口名可以体现功效。  
复制代码
  1. 代码死,人要灵活,观察到语言的规则和现象,都可以被利用。
function isCrushed() {} 
在压缩情况下isCrushed.name !== 'isCrushed'
复制代码
  1. 边界校验,是保证代码质量的必要手段。

    js是动态语言,所以变量的类型运行时可被任意改变,类型校验就更必要了。

优点

  1. redux自身体积很小,小几百行代码,对于一个大型项目来说,体积上简直是冰山一角,所以不必担心包大小。
  2. flux架构的践行者,结合 react,mvc工作流,管理你的应用状态。
  3. 单向流,以前总觉的单向流用起来很不爽快,状态链任性组织,随着工程越来越大,维护也越来越难,难到自己写的代码都看不清晰。所以要让你的应用易于组织,易于维护,那就让你的数据流尽量简单。就像,世间凡事大河,定然不会有勾缠旋绕,方能源远流长,万流汇聚。
  4. 源码读起来很流畅,了解flux架构后,完全可以自己阅读源码进行学习。

不足

  1. 细粒度控制不够好,对于状态的更新,监听(subscribe)都是全量的,可以比较mobx。
  2. 不支持异步操作,需要配合thunk,saga等中间件。
  3. 单向流意味着灵活度的降低,当然这是易于组织维护的代价,并且需要额外增加许多模版代码。
  4. reducers的合并也仅仅支持到一维,超复杂场景或许需要一个树?结构的reducer。

转载于:https://juejin.im/post/5c949efde51d4533594fd314

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值