redux源码分析

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

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

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源码分析


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

查看所有标签

猜你喜欢:

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

创投之巅——中国创投精彩案例

创投之巅——中国创投精彩案例

投资界网站 / 人民邮电出版社 / 2018-11 / 69.00

中国的科技产业发展,与创投行业密不可分。在过去的几十年间,资本与科技的结合,缔造了众多创业“神话”。回顾这些科技巨头背后的资本路径,可以给如今的国内创业者很多有益的启发。 本书从风险投资回报率、投资周期、利润水平、未来趋势等多个维度,筛选出了我国过去几十年中最具代表性的创业投资案例,对其投资过程和企业成长过程进行复盘和解读,使读者可以清晰地看到优秀创业公司的价值与卓越投资人的投资逻辑。一起来看看 《创投之巅——中国创投精彩案例》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具