Redux源码解读

"200行代码,短小精悍。"

Posted by Shiki on July 4, 2018

Redux 核心概念

  • 单一 store ,只能挺过 getState()获取状态,不能直接修改
  • 只能通过触发 action 修改状态
  • 使用纯函数 reducers 描述 action 如何改变 state

整个 redux 的实现就是围绕上面的这三点进行实现的,整个源码量不大,理解了核心概念后去看源码会发现源码的结构逻辑非常清晰,代码中的注释也非常全面,本篇文章会按照如何实现核心概念的方式去阅读源码。(默认你已经会使用 redux 了)

Reducer

我们首先来看 reducer 在 redux 中的使用来更加深入理解 reducer 的作用

currentState = currentReducer(currentState, action)

这是 redux 中唯一使用到 reducer 的地方,在 dispatch 函数中,将旧的 state 和 dispatch 的 action 传入得到新的 state,这和 redux 对于 reducer 的描述是一模一样的,所以我们在编写 reducer 函数的时候一定要严格遵循 redux 的规范写成纯函数,这样可以保证你的 state 变化可以被方便的追踪到。

Action

我们所有的 action 都要通过 dispatch 所以我们直接来看 dispatch 的实现,比较有意思的就是 redux 内其实是有一把同步锁 isDispatching ,这样的做法避免掉了很多处理并发的逻辑,例如并发会带来读脏数据的风险,在看源码之前是根本不知道 redux 有这样的一个设计的。

function dispatch(action) {
    // 省略了一些错误aciton的检测代码 总结下来就是aciton一定是对象并且有type属性
    // isDispatching 类似于同步锁,保证每次修改state的原子性
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    try {
      isDispatching = true
      //获取新的state
      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
  }

Store

redux 最重要的部分就是 createStore,他将存储 state,把 reducer 和 action 组合在一起。

// 接收 reducer 初始state applyMiddleware添加中间件后返回的函数
export default function createStore(reducer, preloadedState, enhancer) {
  // 解决我们不传 preloadedState 第二个参数是 enhancer 的情况
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 在 applyMiddleware 中enhancer做的事情就是添加中间件增强dispatch的能力
    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }
  //store中的转态
  let currentReducer = reducer
  //我们的state树
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  ...
  //返回 store对象以及api
  return {
    //触发aciton使用
    dispatch,
    //添加state改变的回调使用
    subscribe,
    //获取 state
    getState,
    //替换 reducer
    replaceReducer,
    [$$observable]: observable
  }

总结

整个 redux 源码看下来大概只需要一个小时,但是整体的收获还是非常大的,我们可以看到 redux 作者是从核心概念入手,一点点将其转化为工具的。