欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Redux 排序与分析 [I: 减速器与调度]。

最编程 2024-05-01 07:36:21
...

本文适合已经会使用redux的同学阅读。好吧,其实就是自己梳理写给自己看的,顺便分享一下~

最近在知乎上看到了一篇整理讨论Vuex、Flux、Redux、Redux-saga、Dva、MobX这些状态管理库的设计思想的文章,链接在此

想到以前最开始学习React的时候,被React全家桶折磨的那么痛苦,虽然当时也看过别人分享过Redux的分享,也稍微看了一些源码,目前已经忘记了差不多了。虽然网上已经有太多的分享和整理,但是主要是想写给自己看,温故而知新~

Redux 的基本用法

抛开 reactRedux非常简单,以官方github上的示例为样板,稍微改变点东西,以方便我在本地进行调试,我直接把静态资源的redux.js下载到本地,然后进行代码调试。下面是最简单的使用示例:

const type = {
  increment: 'INCREMENT',
  decrement: 'DECREMENT',
}

const initialState = {
  count: 0
}

const reducer = function (state = initialState, action) {
  switch(action.type) {
    case type.increment:
      return {
        ...state,
        count: state.count + 1
      }
    case type.decrement: 
      return {
        ...state,
        count: state.count - 1
      }
    default:
      return state
  }
}

const store = Redux.createStore(reducer)

store.subscribe(() => console.log(store.getState()))

setTimeout(() => {
  store.dispatch({ type: type.increment })
  // 1
  store.dispatch({ type: type.increment })
  // 2
  store.dispatch({ type: type.decrement })
  // 1
}, 4000)

这个代码就是创建了一个reducer然后,使用createStore创建了一个store,然后监听了变化,只要变化就打印当前的state,最后在setTimeout中进行dispatchstate发生变化。

可以看到这里暂时还没有使用applyMiddleware函数,也就是没有使用中间件,这个放在后面说。

入口函数

先看看源码结构:

Redux src
Redux这个变量上, 挂载了一些属性,可以从示例代码上看到,入口函数就是createStore了。首先看一下index.js入口文件。

import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
...
function isCrushed() {}
...
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}

基本没有什么东西,引入了其他文件的导出,然后汇集一起导出了。

createStore

export default function createStore(reducer, preloadedState, enhancer) {
...
}

可以看到createStore函数,接受三个参数,第一个就是传入的reducer处理函数,第二个是预置的state,第三个用来对createStore函数进行增强的中间件等工具方法了。一般enhancer我们都是传入的是applyMiddleware函数的返回值。

if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
  throw new Error(
    'It looks like you are passing several store enhancers to ' +
     'createStore(). This is not supported. Instead, compose them ' +
     'together to a single function.'
  )
}

if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
  enhancer = preloadedState
  preloadedState = undefined
}

if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')
  }
  return enhancer(createStore)(reducer, preloadedState)
}

函数一进来就会判断第二参数和第三个参数的正确行,这里可以看到,如果第二参数传入的是函数,并且第三个参数没传,这里会把它当成enhancer。这就是我们常这样使用的方式:createStore(reducer, applyMiddleware(...)),只传入两个参数。再之后就是返回enhancer(createStore)(reducer, preloadedState)的结果,这个放在后面说。

if (typeof reducer !== 'function') {
  throw new Error('Expected the reducer to be a function.')
}
let currentReducer = reducer // 当前的 reducer
let currentState = preloadedState // 当前的state,默认为 preloaderState,一般为 undefined
let currentListeners = [] // 监听变化的函数队列
let nextListeners = currentListeners // 变化之后,下一次的监听变化的函数队列
let isDispatching = false // 是否处于 Dispatching 状态

紧接着就是判断了reducer是否为函数,然后声明了几个变量,用来初始化。

中间代码会声明一些操作函数,包括我们常用的dispatch,这些函数基本上就是对上面声明的这些变量在进行操作。我们直接看最后面。

dispatch({ type: ActionTypes.INIT })
return {
  dispatch,
  subscribe,
  getState,
  replaceReducer,
  [?observable]: observable
}

首先在返回之前,调用了dispatch函数,对state进行了初始化,传入了一个对象,包含对type的声明,也就是一个action

dispatch 函数

整个 createStore直接执行的代码没几行,重要的动作在dispatch({ type: ActionTypes.INIT })这句上,看看dispatch的实现。

function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }
  1. 首先判断传入的action必须要是一个字面量对象。
  2. 再判断action是否存在type属性。
  3. 判断是否有其他的dispatch正在执行。
  4. isDispatching设置为true,然后将当前的state和传入的action传入到reducer去执行,然后得到结果赋值给currentState
  5. 执行完第4步,就代表state已经修改完了,所以将isDispatching置为false
  6. currentListeners赋值为nextListeners,然后进行遍历,执行里面所有的监听回调函数,最后返回action

逻辑比较简单,当我们调用dispatch({ type: ActionTypes.INIT })的时候,最终是将currentState{ type: ActionTypes.INIT }传入到了我们实现的reducer函数里面。

const reducer = function (state = initialState, action) {
  switch(action.type) {
    case type.increment:
       return {
          ...state,
         count: state.count + 1
       }
     case type.decrement: 
       return {
         ...state,
         count: state.count - 1
       }
     default:
       return state
   }
}

这时候,currentStateundefinedtypeActionTypes.INIT,根据代码,可以看到最终返回的是initialState。这就是初始的state的赋值场景了。

在我们的应用代码里面会去调用dispatch函数,其实每次都是直接调用了reducer然后去遍历执行了listeners队列。简单吧,很简单。根据源码反映,只要dispatch函数的执行,listeners就会被执行,并不是state变了才回去执行,由此可以看出redux的监听就是全量的调用,粒度好像有点大哦。

subscribe 函数

对状态进行监听(其实是对执行dispatch监听),是调用的subscribe函数,它还会返回一个值用来取消当前函数的监听。

function ensureCanMutateNextListeners() {
  if (nextListeners === currentListeners) {
    nextListeners = currentListeners.slice()
  }
}

function subscribe(listener) {
  if (typeof listener !== 'function') {
    throw new Error('Expected the listener to be a function.')
  }
  ...
  let isSubscribed = true

  ensureCanMutateNextListeners()
  nextListeners.push(listener)
  
  return function unsubscribe() {
    if (!isSubscribed) {
      return
    }
    if (isDispatching) {
      throw new Error(
        'You may not unsubscribe from a store listener while the reducer is executing. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }
    isSubscribed = false
    ensureCanMutateNextListeners()
    const index = nextListeners.indexOf(listener)
    nextListeners.splice(index, 1)
    currentListeners = null
  }
}
  1. 判断 listener 是否为函数。
  2. 将当前的isSubscribed标志,置为true
  3. 调用ensureCanMutateNextListeners函数对currentListeners进行浅拷贝,赋值给nextListeners
  4. 将传入的listenernextListeners队列。
  5. 返回unsubscribe函数,这个函数左右就是在 nextListeners 队列中找到listener然后进行删除。

getState 函数

直接将currentState变量的值进行了返回。

function getState() {
  if (isDispatching) {
    throw new Error(
      'You may not call store.getState() while the reducer is executing. ' +
        'The reducer has already received the state as an argument. ' +
        'Pass it down from the top reducer instead of reading it from the store.'
    )
  }
  return currentState
}

replaceReducer 函数

传入一个新的reducer函数,进行替换,最后调用一下dispatch函数,得到reducer对应的state

function replaceReducer(nextReducer) {
  if (typeof nextReducer !== 'function') {
    throw new Error('Expected the nextReducer to be a function.')
  }
  currentReducer = nextReducer
  // This action has a similiar effect to ActionTypes.INIT.
  // Any reducers that existed in both the new and old rootReducer
  // will receive the previous state. This effectively populates
  // the new state tree with any relevant data from the old one.
  dispatch({ type: ActionTypes.REPLACE })
}

observable 函数

这个函数,我没有使用过,暂时没有碰到相关的场景,根据源码,我理解为,类似于subscribe,但是这个监听回调可以结合符合约定的第三方observable库。比如源码注释提到的https://github.com/tc39/proposal-observable

function observable() {
  const outerSubscribe = subscribe
  return {
    subscribe(observer) {
      if (typeof observer !== 'object' || observer === null) {
        throw new TypeError('Expected the observer to be an object.')
      }
      function observeState() {
        if (observer.next) {
          observer.next(getState())
        }
      }
      observeState()
      const unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },
    [?observable]() {
      return this
    }
  }
}

小结

以上梳理了没有使用中间件增强的基本流程,让我最深刻的就只有listener的调用,只是和dispatch调用有关,至于state有没有修改只是简单赋值而已。

redux应用了很多函数式的编程技巧,就比如subscribe函数最后直接返回取消订阅的函数。

原文地址

未完待续~

推荐阅读