redux设计思想与工作原理

168 阅读4分钟

redux是什么

redux是全局状态管理器,会将所有的状态都存在store中。

redux三大原则

  1. store: 单一数据源,全局只有一个store树
  2. action: state只读的,修改state的唯一方式,就是通过dispatch派发一个action动作
  3. reducer: 使用纯函数来修改,就收两个参数reducer(state,action),返回新的数据给store

工作流程

  1. 在页面的组件中,利用store的dispatch()方法,发送一个action
  2. redux接收到action后,分发给对应的reducer函数
  3. reducer处理完成后,返回新的state对象。这个state会更新到store对象里
  4. redux接收到新的store,通知store.subscribe()函数执行,进而驱动视图层作出对应的改变

state数据更改后,如何通知页面渲染

  1. 利用store.subscribe订阅一次redux的store,当下次store变化后,触发订阅函数执行checkForUpdates
  2. 通过store.getState()获取最新的state值,通过equalityFn函数比较newSelectedState和latestSelectedState,如果有变化就执行forceRender,触发react创建update对象,强制渲染;否则直接return

源码解读

  1. createStore(): createStore(reducer,preloadedState, enhancer)生成store对象
  2. combineReducers(): 将多个reducer合并起来。
  3. applyMiddleware(): applyMiddleware() 函数是一个增强器,组合多个中间件,最终增强store.dispatch函数,dispatch时,可以串联执行所有中间件。
  4. compose(): 用于把接收到的函数从右向左进行组合。
  5. bindActionCreators(): bindActionCreators(actionCreators, dispatch) 生成actions, 用于将传入的actionCreator与dispatch方法相结合,揉成一个新的方法

redux是如何工作的-createStore

const store = Redux.createStore(reducer,initState,applyMiddle)
作用:createStore创建一个store对象
参数:reducer处理函数 initState初始状态数据 指定中间件

再看store上的几个方法

  1. getState(): getState() 获取存在createStore函数内部闭包的对象。
  2. dispatch(): (派发),也就是把subscribe收集的函数,依次遍历执行
  3. subscribe(): (订阅),订阅收集函数存在数组中,等待触发dispatch依次执行。返回一个取消订阅的函数,可以取消订阅监听。
  4. replaceReducer(): replaceReducer(nextReducer) 主要用于redux开发者工具,对比当前和上一次操作的异同。有点类似时间穿梭功能。

从拿到入参到返回一个store过程

  1. 调用createStore
  2. 处理没有传入初始状态的情况
  3. 若enhancer不为空,则用enhancer包装createStore
  4. 定义内部变量currentReducer、currentState、currentListeners、nextListeners、isDispatching
  5. 定义ensureCanMutateNextListeners()改方法用于确保currentListeners与nextListeners不指向统一个引用
  6. 定义getState方法,用于获取当前的状态
  7. 定于subscribe,用于注册listeners,用于订阅监听函数
  8. 定义dispatch方法,用于派发action,调用reducer并触发订阅
  9. 定义replaceReducer方法,用于替换reducer
  10. 在replaceReducer中,执行一次dispatch,完成状态的初始化
  11. 定义observable,可忽略

redux是如何工作的-dispatch工作流程

  1. 调用dispatch,入参为action对象
  2. 前置校验。约束action中必须有type属性作为action的唯一标识
  3. 判断,如果当前已经位于dispatching流程中,则不允许再发起dispatch
  4. 执行reducer前先上锁,将isDispatching置为true。
  5. 调用reducer,计算新的state
  6. “解锁”,将dispatching置为false
  7. 触发订阅。 const listeners = (currentListeners = nextListeners);
  8. 返回action

注意:在调用 reducer 之前,Redux 首先会将 isDispatching 变量置为 true,待 reducer 执行完毕后,再将 isDispatching 变量置为 false。这里之所以要用 isDispatching 将 dispatch 的过程锁起来,目的是规避**“套娃式**”的 dispatch。
更准确地说,是为了避免开发者在 reducer 中手动调用 dispatch。 若真的在 reducer 中调用 dispatch,那么 dispatch 又会反过来调用 reducer,reducer 又会再次调用 dispatch......这样反复相互调用下去,就会进入死循环,属于非常严重的误操作(可理解为发布订阅的缺点)。

subscribe 是如何与 Redux 主流程结合的呢?

dispatch 中执行的 listeners 数组从订阅中来,而执行订阅需要调用subscribe。在实际的开发中,subscribe并不是一个严格必要的方法,只有在需要监听状态的变化时,我们才会调用 subscribe。
subscribe 在订阅时只需要传入监听函数,而不需要传入事件类型。这是因为 Redux 中已经默认了订阅的对象就是“状态的变化(准确地说是 dispatch 函数的调用)”这个事件。

首先,我们可以在 store 对象创建成功后,通过调用 store.subscribe 来注册监听函数,也可以通过调用 subscribe 的返回函数来解绑监听函数,监听函数是用 listeners 数组来维护的;当dispatch action 发生时,Redux 会在 reducer 执行完毕后,将 listeners 数组中的监听函数逐个执行。这就是 subscribe 与 Redux 主流程之间的关系

参考文章: blog.51cto.com/u_10887428/…