Redux源码解析

193 阅读2分钟

Redux主要做的几件事情

  • 生成一个全局唯一的state
  • 仅提供一种方法去修改state
  • 使用中间件加强dispatch函数
  • 提供listener在数据变化的时候执行相应的操作

使用闭包在createStore函数保存唯一的state

createStore.js

输出:

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

这些函数会调用到闭包里面的一些变量,如currentState, currentReducer, currentListeners。

仅提供dispatch去修改state

dispatch

作用:将action和state传入reducer并修改对应的state,并执行listeners数组里面的所有listener 核心就是这句

currentState = currentReducer(currentState, action)

combineReducers.js

作用:将一堆reducer存在闭包里面,最终返回一个总的reducer
输入:一个对象,对象中每个属性对应一个reducer

{
    task: (state, action) => {
        switch (action.type) {
            case 'CHANGE_FILTER_STATUS':
                state.filterStatus = action.data
                return state
            default:
                return state;
        }
    }
}

核心代码:

// 将reducer存到闭包中
const finalReducers = {}
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]
    }
}
 
// 输出:一个总的recuder函数,作用为遍历所有的reducer,返回一个state
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
  }

通过applyMiddleware加强dispatch函数

核心代码:

const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

其中compose其实就是使用array.reduce从右到左执行函数
compose核心代码:

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

通过subscribe注册listener

每次添加listener时都先深复制复制一份之前的listener
返回一个unsubscribe函数

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

用array.slice()方法来深复制

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

总结

通过分析redux的源码,首先我们可以更好的理解我们使用redux的时候,里面具体发生了什么事情,其次是可以更清楚地看到各种redux的插件是如何加入到redux里面的,对于我们以后使用redux或者redux的插件会有帮助。
另一方面,从代码的组织上来看,可以看到了里面有很多闭包的使用,还有各种各样的错误处理,以及简洁的语法,对于自身代码质量的提高也有一定的影响。