Bobolo
  • Home
  • Me

Redux

浅析 Redux 源码

7 Min Read

Mon Dec 23 2019

Written By Bobolo

Redux,非常出名的状态管理库,它并不是只能用在 React,而使用 React 的会用到 React-Redux,虽然已经有很多原理、源码的分析文章。但我带着从它源码出发并且添加注释,来去理解和掌握它的思想。

首先需要明白 Redux 和 React Redux 的区别, Redux 是一个状态管理容器,它不属于任何一个框架,而 React Redux 是在 Redux 的基础上结合了 React。

Published on npm

Redux

Redux Analyze,完整的代码注释和 Example 可以看代码,文章中的浅析只保留关键逻辑方便理解,会将大部份非重要判断去除。如

  • isDispatching,是否在 dispatch 中或者在 createStore 中
  • isPlainObject,是否纯对象
  • function,传入的 reducer 是否 function
  • isSubscribed,是否已经订阅

createStore

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // NOTE:enhancer正常情况
    return enhancer(createStore)(reducer, preloadedState)
  }

  let currentReducer = reducer //NOTE:当前Reducer
  let currentState = preloadedState //NOTE:当前State
  let currentListeners = [] //NOTE:当前监听回调的数组
  let nextListeners = currentListeners //NOTE:下一个监听回调的数组

  // NOTE:保证可以修改下一个监听组的数据不出错,在需要使用到nextListeners的地方都需要进行处理;
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      // NOTE:防止指针问题,返回了新的数组
      nextListeners = currentListeners.slice()
    }
  }

  // NOTE:返回当前整个Redux的State
  function getState() {
    return currentState
  }

  function subscribe(listener) {
    ensureCanMutateNextListeners() // NOTE:确保nextListeners的准确性
    nextListeners.push(listener) // NOTE:添加listener到nextListeners

    // NOTE:返回解绑的回调,可以解除当前listener的监听;
    return function unsubscribe() {
      // NOTE:设置订阅状态,并将listener在nextListeners中剔除;
      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    // NOTE:将dispatch状态修改,并且根据reducer里面注册好的的function,拿到dipacth的新state,设置为当前状态
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    // NOTE:执行整个订阅的事件组
    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 }) // NOTE:替换完成后dispatch
  }

  //NOTE:是一个第三方库,https://github.com/tc39/proposal-observable,主要用户对订阅的对象扩展处理
  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        function observeState() {
          // NOTE:如subscribe(observer).filter(getState() => { dosomething }).map(getState() => { dosomething })
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      // NOTE:$$observable为一个Symbol对象
      [$$observable]() {
        return this
      },
    }
  }

  // NOTE:在创建Store时,dispatch一个初始化的Action
  dispatch({ type: ActionTypes.INIT })

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

Compose

Compose 是值得大家去理解或者记住的

// NOTE:该方法主要是实现函数Curry化,并且通过reduce,将全部参数(function)从右到左执行,并且将执行后的return作为下一个的参数传入
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  /*
    NOTE:reduce就是数组叠加,主要将funcs处理好
    假设a、b、c都为function,compose则会处理成arg => a(b(c(arg))),这样一个function,compose(args)即可得到最终结果
  */
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  )
}

combineReducers

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    // NOTE:如果是正常的Reducer直接塞入finalReducers
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  return function combination(state = {}, action) {
    let hasChanged = false
    const nextState = {}
    // NOTE:拿到整个Reducers,然后拿到对应的Key,最后拿到key对应的reducer,然后传入state, action并执行reducer方法
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key] //NOTE:获取当前Reduce对应的State
      const nextStateForKey = reducer(previousStateForKey, action) //NOTE:获取传入action,Reduce对应的State
      nextState[key] = nextStateForKey // NOTE:将更新后的State当道nextState对应的key中
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // NOTE:如果前后的State不等就return nextState,否则return state
    return hasChanged ? nextState : state
  }
}

bindActionCreators

// NOTE:将action和dispatch进行封装,方便用户直接定义变量使用
function bindActionCreator(actionCreator, dispatch) {
  return function () {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  /* 
    NOTE:如果是object,则进行遍历,对action里面的value为function的进行处理,主要是做actions的处理
    {
      userAction: () => { type, data },
      homeAction: () => { type, data }
    }
  */
  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

applyMiddleware

export default function applyMiddleware(...middlewares) {
  return createStore =>
    (...args) => {
      const store = createStore(...args)
      // NOTE:中间件的Api,目前只要能拿到store的状态和将dispatch传递下去即可
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      }
      // NOTE:给每个中间件拿到Store
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
      // NOTE:将所有中间件合并起来,从右往左处理,执行完的作为参数传给下一个中间件
      dispatch = compose(...chain)(store.dispatch)

      return { ...store, dispatch }
    }
}

Flow

redux

Redux Thunk

/**
 * @param {object} extraArgument 主要是用户需要传递的参数
 * return 一个curry的函数
 */
function createThunkMiddleware(extraArgument) {
  /**
   * NOTE:结合redux的applyMiddleware来看
   * @param {({ dispatch, getState })} middlewareAPI { getState: store.getState, dispatch: (...args) => dispatch(...args) }
   * @param { next } store.dispatch
   * @param { action } any 可以是object或者function,由用户传入 
   * 
   * applyMiddleware将api的设置处理好,然后将store.dispatch传入
   * 再return action => {
      if (typeof action === 'function') {
        return action(dispatch, getState, extraArgument)
      }
      return next(action)
    }
    用户的action如果传入的是function则将dispatch, getState, extraArgument传递并执行该方法,所以需要在该方法上去dispatch
    如果传入的不是function则直接dispatch
  */
  return ({ dispatch, getState }) =>
    next =>
    action => {
      if (typeof action === 'function') {
        return action(dispatch, getState, extraArgument)
      }
      return next(action)
    }
}

// NOTE:默认的不使用extraArgument,需要增加参数的的直接使用thunk.withExtraArgument
const thunk = createThunkMiddleware()
thunk.withExtraArgument = createThunkMiddleware

export default thunk
Powered by Bobolo

Copyright © Bobolo Blog 2021