Redux 的计划理念
Redux 的计划采用了 Facebook 提出的 Flux 数据处置惩罚理念
在 Flux 中通过建立一个公共集中数据仓库 Store 举行管理,整体分成四个部门即: View (视图层)、Action (动作)、Dispatcher (派发器)、Store (数据层)
如下图所示,当我们想要修改仓库的数据时,需要从 View 中触发 Action,由 Dispatcher 派发到 Store 修改数据,从而驱动视图更新
这种计划的利益在于其数据流向是单一的,数据的修改一定是会经过 Action、Dispatcher 等动作才华实现,方便猜测、维护状态的流向。
当我们相识了 Flux 的计划理念后,便可以照葫芦画瓢了。
如下图所示,在 Redux 中同样需要维护一个公共数据仓库 Store, 而数据流向只能通过 View 触发 Action、 Reducer更新派发, Store 改变从而驱动视图更新
工作原理
当我们相识了 Redux 的计划理念后,趁热打铁炫一波 Redux 的工作原理,我们知道使用 Redux 举行状态管理的第一步就是需要先创建数据仓库 Store, 也就会需要调用 createStore 方法。那我们就先拿 createStore 开炫。
createStore
从 Redux 源码中我们不难看出,createStore 接收 reducer、初始化state、中间件三个参数,当执行 createStore 时会记录当前的 state 状态,并返回 store 对象,包罗 dispatch、subscribe、getState 等属性。
此中
- dispatch: 用来触发 Action
- subscribe: 当 store 值的改变将触发 subscribe 的回调
- getState: 用来获取当前的 state 状态。
getState 比力简朴,直接返回当前的 state 状态,接下来我们将着重相识 dispatch 与 subscribe 的实现。
- function createStore(reducer, preloadedState, enhancer) {
- let currentReducer = reducer // 记录当前的 reducer
- let currentState = preloadedState // 记录当前的 state
- let isDispatching = false // 是否正在进行 dispatch
-
- function getState() {
- return currentState // 通过 getState 获取当前的 state
- }
-
- // 触发 action
- function dispatch(action: A) {}
-
- function subscribe(listener: () => void) {}
- // 初始化 state
- dispatch({ type: ActionTypes.INIT } as A)
-
- // 返回一个 sttore
- const store = {
- dispatch: dispatch as Dispatch<A>,
- subscribe,
- getState
- }
- return store
- }
复制代码 dispatch
在 Redux 中, 修改数据的唯一方式就是通过 dispatch,而 dispatch 接受一个 action 对象作为参数,执行 dispatch 方法,将生成新的 state,并触发监听事件。
- function dispatch(action) {
- // 如果已经在触发中,则不允许再次出发 dispatch (禁止套娃)
- // 例如:在 reducer 中触发 dispatch
- if (isDispatching) {
- throw new Error('Reducers may not dispatch actions.')
- }
- try {
- // 上锁
- isDispatching = true
- // 调用 reducer,获取新的 state
- currentState = currentReducer(currentState, action)
- } finally {
- isDispatching = false
- }
- // 触发订阅事件
- const listeners = (currentListeners = nextListeners)
- listeners.forEach(listener => {
- listener()
- })
- return action
- }
复制代码 subscribe
在 Redux 中, 可以通过 subscribe 方法来订阅 store 的厘革, 一旦 store 发生了厘革, 就会执行订阅的回调函数
可以看到 subscribe 方法接收一个回调函数作为参数, 执行 subscribe 方法将会返回一个 unsubscribe 函数, 用于取消订阅
- function subscribe(listener: () => void) {
- if (isDispatching) {
- throw new Error()
- }
- let isSubscribed = true // 防止调用多次 unsubscribe
- ensureCanMutateNextListeners() // 确保 nextListeners 是 currentListeners 的快照,而不是同一个引用
- const listenerId = listenerIdCounter++
- nextListeners.set(listenerId, listener) //nextListeners 添加订阅事件
- // 取消订阅事件
- return function unsubscribe() {
- if (!isSubscribed) {
- return
- }
- if (isDispatching) {
- throw new Error()
- }
- isSubscribed = false
- ensureCanMutateNextListeners(); // 如果某个订阅事件执行了 unsubscribe, nextListeners 创建了新的内存地址,而原先的listeners 依然保持不变 (dispatch 方法中的312 行)
- nextListeners.delete(listenerId)
- currentListeners = null
- }
- }
复制代码 ensureCanMutateNextListeners 与 currentListeners 的作用
承接上文,在 subscribe 中不管是注册监听还是取消监听都会调用 ensureCanMutateNextListeners 的方法,那么这个方法是做什么的呢?
从函数的逻辑上不难过出答案:
ensureCanMutateNextListeners 确保 nextListeners 是 currentListeners 的快照,而不是同一个引用
- function ensureCanMutateNextListeners() {
- if (nextListeners === currentListeners) { // currentListeners 用来确保循环的稳定性
- nextListeners = new Map()
- currentListeners.forEach((listener, key) => {
- nextListeners.set(key, listener)
- })
- }
- }
复制代码 在 dispatch 或者 subscribe 函数中,都是通过 nextListeners 触发监听,那为何还需要使用 currentListeners?
这里就不卖关子了,这里的 currentListeners 用于确保在 dispatch 中 listener 的数目不会发生厘革, 确保当前循环的稳固性。
请看下面的例子 |