Redux-源码解析

简介:

整个redux的源码非常简单,所以解释很少,看代码就能看明白......

combineReducers


为啥先说这个

前提先看middleware: Redux-Middleware-源码解析, 这些都是解析createStore的前提


//传入的reducers是个对象
//将多个reducer捆成一个
export default function combineReducers(reducers) {
  //取出所有reducer的key=> {key1:value,key2:value}
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    //取出key
    const key = reducerKeys[i]

    if (typeof reducers[key] === 'function') {
      //可以代表的value是函数的话,保存进 finalReducers 对象中
      finalReducers[key] = reducers[key]
    }
  }

  //取出所有value是函数的key
  const finalReducerKeys = Object.keys(finalReducers)

  let shapeAssertionError
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }
  //返回一个 combination 函数,方法接收state,action参数 这个返回的函数是createStore的第一个参数
  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    let hasChanged = false
    const nextState = {}
    //循环每一个reducer,
    for (let i = 0; i < finalReducerKeys.length; i++) {
      //取出key
      const key = finalReducerKeys[i]
      //取出key对应的reducer
      const reducer = finalReducers[key]
      //根据key在state获取状态
      //state的状态都是根据reducer的名字进行存储的
      const previousStateForKey = state[key]
      //将state和action传入reducer中
      const nextStateForKey = reducer(previousStateForKey, action)
      //将nextStateForKey reducer 放入到nextState中
      nextState[key] = nextStateForKey
      //在state中的状态和reducer中返回的状态是否一样
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //返回新的state
    return hasChanged ? nextState : state
  }
}

是什么

  1. 名字的含义就是组合reducer
  2. 因为reducers是作为createStore函数的第一个参数,而不是前多少个参数

干了什么


export default combineReducers({
    userinfo,
    stores,
    home,
    likeList
})

  1. 其中的每一个参数都是一个reducer,而且每一个参数的名字都作为在store中的key
  2. 将这些reducer组合成一个reducers,提供给createStore作为参数

流程啥样

  1. 虽然传了很多reducer但是先过滤掉不是function的reducer(不知道不是function的reducer是啥)
  2. 将过滤的key存放到finalReducerKeys中,将过滤的reducer存放到finalReducers中,闭包要用
  3. 然后返回一个 combination() 函数,其实这个才是createStore的第一个参数

combination做了什么

  1. 循环每一个有效的reducer
  2. 取出reducer,state中的对应的状态
  3. 调用reducer,并传入取出的状态,和action,获取返回值(新state)
  4. 返回新的state

createStore


//创建store的api,也是redux中最重要的api,而创建的store用于管理应用中的所有state,且只有一个store
export default function createStore(reducer, preloadedState, enhancer) {
  //简而言之,第二个参数是函数,并且第三个参数是undefined,然后第二第三个参数值互换
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    //这个就是中间件,看middleware那篇文章
    return enhancer(createStore)(reducer, preloadedState)
  }

  //保存当前的reducer
  let currentReducer = reducer
  //保存传入的状态
  let currentState = preloadedState
  //设置当前监听集合
  let currentListeners = []
  //将当前的监听集合赋值给下一个监听集合
  let nextListeners = currentListeners
  let isDispatching = false
  
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      //slice() 没有参数默认begin为0 就是拷贝下
      nextListeners = currentListeners.slice()
    }
  }

  //获取当前状态
  //函数嵌套函数,内部函数引用外部函数的变量,最后返回函数,这是闭包
  function getState() {
    return currentState
  }

  //增加监听,参数listener是一个回调函数,在dispatch里,会调用所有的监听器
  function subscribe(listener) {
    let isSubscribed = true

    ensureCanMutateNextListeners()
    //把新增加的监听加入到当前监听列表中
    nextListeners.push(listener)
    //返回一个函数,用于去除监听
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      //解除监听 isSubscribed 设置为false,意为已经取消监听
      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      //然后干掉这个监听
      nextListeners.splice(index, 1)
    }
  }

  //这个是比较常用的api=>主要用于触发action,改变状态
  function dispatch(action) {
    try {
      isDispatching = true//正在dispatch
      //执行reducer 返回新state,调用的是combination()方法
      currentState = currentReducer(currentState, action)
    } finally {
      // finally不管报没报错最后都要执行
      isDispatching = false
    }
    //执行所有监听
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }
  //替换reducer 然后更新store
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  //这个是留给内部
  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

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

      [$$observable]() {
        return this
      }
    }
  }
  //初始化store
  dispatch({ type: ActionTypes.INIT })

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

是什么

  1. 这个是最核心的api,这个函数是创建store的唯一方式
  2. 并且返回了dispatch,subscribe,getState...比较常用的api

干了什么

const store = createStore(rootReducer, applyMiddleware(...middlewares));
复制代码
  1. 如果有中间件,先处理中间件然后回过头来在执行createStore
  2. 保存了一些当前的属性,用于闭包,提供的api

api

getState

这个直接返回了当前的state,因为闭包,所以可以获取到最新的state

subscribe

负责新增监听,参数是个回调函数,将回调函数加入到监听集合,并且返回一个取消监听的函数:这个返回的函数内部封装了当执行时,在监听集合中将这个监听去掉

dispatch

是最核心的api,通过执行reducer(闭包,currentReducer),返回新的state,并赋值给当前的currentState,并执行所有的监听,返回action

replaceReducer

用于替换reducer,然后调用dispatch更新state


bindActionCreators

这个结合connect使用,在mapDispatchToProps中定义,就可以直接用this.props.userInfoActions来调用dispatch(action)操作


function mapStateToProps( state ) {
    return {}
}
function mapDispatchToProps( dispatch ) {
    return {
        userInfoActions : bindActionCreators(userInfoActionsFormOtherFile, dispatch)
    }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(App)
复制代码function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  //如果 actionCreators 是一个函数,染回 dispatch(actionCreator.apply(this, arguments))
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  //actionCreators 不是函数,不是对象,不是null
  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)
    }
  }
  //最后迭代actionCreators里的元素,如果是函数,就按照函数的方式组成数组,返回
  return boundActionCreators
}

是什么

就跟名字一样:给actionCreators绑定dispatch,也就是返回一个dispatch(actionCreator.apply(this, arguments))这样的函数,或者包含多个dispatch的数组

干了什么

  1. 传入两个参数,第一个是actionCreator,第二个是dispatch
  2. 如果只是一个actionCreator,那么直接返回一个包含dispatch(actionCreator.apply(this, arguments))的函数
  3. 如果他是个数组,就去循环这个数组将每一个actionCreator都绑定dispatch,然后返回这个数组

总结

看完了源码,感觉redux真的没有太多的东西,真是映射了那句话:用尽量少的代码,做尽量多的事情!

combineReducers

  1. 参数reducers是一个多个reducer组合的对象
  2. 去除所有的reducer的key=> reducerKeys
  3. 循环reducerKeys通过key取出reducer
  4. 如果reducer是个函数,就加进 finalReducers 中
  5. 取出finalReducers中的所有key=>finalReducerKeys
  6. 然后返回combination,这个是个内部函数,里面用到了上面的finalReducers和finalReducerKeys
  7. combination中取出每个reducer,根据finalReducerKeys中对应的key,在state中取出响应的状态
  8. 执行reducer并将参数传入返回新状态
  9. 将返回的状态添加进nextState对象中
  10. 返回新状态

通过代码的执行总结combineReducers

  1. 将多个reducer的对象reducers传入combineReducers中
  2. 返回 combination函数
  3. 返回的函数作为createStore的第一个参数
  4. 进入createStore中,提供的dispatch()中用到了reducers,并传入了state和action
  5. 然后执行combination函数,循环finalReducers
  6. 执行每一个reducer,通过返回的state,并添加进nextState对象中
  7. 返回的state和参数的state比较,如果两个state相等,则说明这个key所对应的state没有变化
  8. 如果不相等说明有变化,标记有变化
  9. 最后根据state是否有变化决定返回 nextState 还是参数 state


原文发布时间为:2018年06月26日
原文作者:mazy
本文来源:  掘金  如需转载请联系原作者

相关文章
|
2天前
PandasTA 源码解析(二十三)
PandasTA 源码解析(二十三)
8 0
|
2天前
PandasTA 源码解析(二十二)(3)
PandasTA 源码解析(二十二)
5 0
|
2天前
PandasTA 源码解析(二十二)(2)
PandasTA 源码解析(二十二)
10 2
|
2天前
PandasTA 源码解析(二十二)(1)
PandasTA 源码解析(二十二)
8 0
|
2天前
PandasTA 源码解析(二十一)(4)
PandasTA 源码解析(二十一)
7 1
|
2天前
PandasTA 源码解析(二十一)(3)
PandasTA 源码解析(二十一)
8 0
|
2天前
PandasTA 源码解析(二十一)(2)
PandasTA 源码解析(二十一)
9 1
|
2天前
PandasTA 源码解析(二十一)(1)
PandasTA 源码解析(二十一)
9 2
|
2天前
PandasTA 源码解析(二十)(1)
PandasTA 源码解析(二十)
7 0
|
2天前
PandasTA 源码解析(十九)(3)
PandasTA 源码解析(十九)
10 2

推荐镜像

更多