redux源码分析

栏目: 编程工具 · 发布时间: 7年前

内容简介:这个大家都很熟悉了吧,就不再讲了

Redux is a predictable state container for JavaScript apps. 官网第一句就很全面的介绍了redux。一个可预测的状态管理工具。redux 是如何做到的呢?

  • 单一的数据源 (states)
  • states是只读且不可变化的 (每次改变返回新值)
  • 通过纯函数改变states (reducer, 无副作用, 相同的输入就有相同的输出)
  • 改变states方式具有可描述性且单一 (action)

数据流

redux源码分析

这个大家都很熟悉了吧,就不再讲了

源码解读

createStore

func createStore(reducer, preloadedState, enhancer) -> ({ dispatch, subscribe, getState, replaceReducer,[$$observable]: observable })

export default function createStore(reducer, preloadedState, enhancer) {
  // preloadedState 可以不传,确定真实的参数
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    return enhancer(createStore)(reducer, preloadedState)
  }

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

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

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  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
  }

  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  function observable() {
    //...
  }

  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable,
  }
}
复制代码

为了篇幅减少些,上面的代码我删掉了部分错误检查。其实都很好理解,就有一点需要注意一下,为什么要用两个变量(currentListeners,nextListeners)来储存listener。这是因为redux允许在subscirbe中执行unsubscribe。 例如:

const unsubscribe1 = store.subscribe(() => {
  unsubscribe1()
})

const unsubscribe2 = store.subscribe(() => {
  unsubscribe2()
})

dispatch(unknownAction);
复制代码

如果不缓存dispatch的listener的话 那么在dispatch里循环listeners时就会有问题。 另外也可以发现,如果你绑定了多个subscribe函数,即使在第一个subscription里执行了所有的unSubscribe,subscription还是会全部执行一遍 另外 observable 是为了和其他一些observable库配合使用,当目前为止还没用过。

applyMiddleware

用法

const logger = ({ getState }) => next => action => {
  console.log('will dispatch logger1', action)
  const returnValue = next(action)
  console.log('state after dispatch logger1', getState())
}

const logger2 = ({ getState }) => next => action => {
  console.log('will dispatch logger2', action)
  const returnValue = next(action)
  console.log('state after dispatch logger2', getState())
}

const store = createStore(
  todos,
  preload,
  applyMiddleware(logger1, logger2),
);
// will dispatch logger1
// will dispatch logger2
// state after dispatch logger2
// state after dispatch logger1
复制代码

源码

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)
    }
    
    // chainItem:next => action => {...}
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

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

// compose(logger1, logger2) => (...arg) => logger1Next(logger2Next(...arg))
// 合成之后的dispatch就成了这个样子:
const dispatch = (action) => logger1Next(logger2Next(store.dispatch))(action);

// 假如还有logger3, 那么应该是这个样子
const dispatch = (action) => logger1Next(logger2Next(logger3Next(store.dispatch)))(action);
复制代码

可以compose原因是middle模式统一:store => next => action => {} 在执行完 const chain = middlewares.map(middleware => middleware(middlewareAPI))之后 chainItem: next => action => {} 本质是 接收一个dispatch,再返回一个合成的dispatch

bindActionCreators

用法

bindActionCreators({ actionCreator1, actionCreator2, ...}, dispatch) => ({ boundAction1, boundAction2, ... })

源码

// 返回 boundAction
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  // 仅仅传入一个actionCreator
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error()
  }

  // 传入一个对象时,绑定所有key,并返回一个对象
  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
}
复制代码

combineReducers

由于redux仅有一个store,所以当项目复杂的时候,数据需要分类,这时就会用到此函数。作用是将多个分开的reducers合并。

原型:(reducer1,reducer2, reducer3,...) => combinedReducer

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  // 遍历检查 reducer 不应该为 undefined
  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]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

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

  let shapeAssertionError
  try {
    // 检查 reducer 必须设置初始值,不可以返回 undefined
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  // combinedReducer
  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
      // 检查 state 是否合法,例如state必须是对象、state应该有相对应的reducer等
      // 使用了combineRudcer之后,state就是对象了,原来的state都放在了相对应的key下面
      // 例如:combineReducers({ todos: reducer1, todoType: reducer2 });
      // store 变成了 { todos: todoState, todoType: todoTypeState };
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {}
    // 将action分发给每个reducer, 如果该改变就返回新的。
    // 否则返回旧值,类似于你在每个reduer中的做法。
    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
  }
}
复制代码

总结

redux源码还是比较好理解的,记住reducer一定要保证是纯函数。这对于测试和与其他的库配合至关重要。例如 react-redux。 感兴趣的朋友可以看看我的react-redux源码分析


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ajax for Web Application Developers

Ajax for Web Application Developers

Kris Hadlock / Sams / 2006-10-30 / GBP 32.99

Book Description Reusable components and patterns for Ajax-driven applications Ajax is one of the latest and greatest ways to improve users’ online experience and create new and innovative web f......一起来看看 《Ajax for Web Application Developers》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具