Redux-源码解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:

整个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月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
92 2
|
14天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
14天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
14天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
58 12
|
1月前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
15天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
3月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
89 0
|
3月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
69 0
|
3月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
75 0

热门文章

最新文章

推荐镜像

更多