Redux原理解析

时间:2020-8-22 作者:admin

Redux概念

Redux的核心本质就是一个发布订阅模式。

Redux原理解析

Redux的三个基本原则:

  1. 单一数据源。在Redux中一般只有一个store,用来存放数据。
  2. 只读的state。更改状态的唯一方法是触发一个动作action,action 描述了这次修改行为的相关信息的对象。由于action只是简单的对象,因此可以将它们记录,序列化,存储并在以后进行调试。
  3. 使用纯函数进行操作更改state。为了描述 action 如何修改状态,需要使用reducer 函数。reducer 函数接收前一次的 state 和 action,返回新的 state, 而不是改变先前的state。只要传入相同 的state 和 action,无论reducer被调用多少次,那么就一定返回相同的结果。

Redux源码浅析

createStore

function createStore(reducer, preloadedState, enhancer) {
  // 删除了一些参数矫正的代码

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // store enhancer的使用,也就是使用传入的中间件
    // enhancer就是后续会提到的applyMiddleware函数
    return enhancer(createStore)(reducer, preloadedState)
  }


  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  // 这个方法用于保持nextListeners和currentListeners的同步
  // nextListeners 是从currentListeners浅拷贝来的。
  // 订阅的时候是操作nextListeners, 防止在dispatch的时候执行subscribe/unsubscribe带来的bug
  // 在dispatch 的时候会同步 currentListeners 和 nextListeners (currentListeners = nextListeners)
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  // 获取当前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
  }

  // 订阅一个listener,是操作在nextListeners上的
  // 返回的是一个函数,用于删除这个订阅的listener,也是在nextListeners上操作的
  // 删除订阅不会在当前的dispatch中生效,而是会在下一次dispatch的时候生效
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }

    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
    }
  }


   // 触发action
   // 1. 执行reducer,更新state
   // 2. 执行订阅的listener
   // 返回的是传入的action, 用于调试记录
  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
  }

  // 替换当前的reducer
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    // 触发一个内部的REPLACE action
    dispatch({ type: ActionTypes.REPLACE })
  }

  // 用于 observable/reactive libraries
  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
      }
    }
  }

  // 触发一个INIT action,用于创建初始state树
  dispatch({ type: ActionTypes.INIT })

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

applyMiddleware 详解

function applyMiddleware(...middlewares) {
  // 返回的函数就是在createStore中调用的那个enhancer
  return (createStore) => (...args) => {
    // 创建store, 可以获取dispatch和getState
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    // 封装传入中间件的api
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args),
    }
    // 给中间件们绑定能dispatch和getState的api
    const chain = middlewares.map((middleware) => middleware(middlewareAPI))

    // 从这里就可以看出middleware就是来加强dispatch的,在dispatch了一个action后搞点事情
    // compose的本质就是用于串联执行中间件
    // 这个dispatch也重写个middlewareAPI调用的dispatch
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch,
    }
  }
}

详解 dispatch = compose(...chain)(store.dispatch)

compose的本质,组合多个函数, 用于串联执行中间件

const compose =(...middlewares) =>  middlewares.reduce((f1, f2) => (...args) => f1(f2(...args)))

先看看标准的middleware

({ dispatch, getState }) => (next) => (action) => {
   // 搞点事情
   const state =  next(action)
   // 搞点事情
   return state
  }

去掉getState和dispatch的实现dispatch = compose(...chain)(store.dispatch)中的chain可以理解为:

const middlewares = [
  (next) => (action) => {
    console.log('middleware 1')
    next(action)
    console.log('middleware 1 after')
  },
  (next) => (action) => {
    console.log('middleware 2')
    next(action)
    console.log('middleware 2 after')
  },
  (next) => (action) => {
    console.log('middleware 3')
    next(action)
    console.log('middleware 3 after')
  },
]

调用上面的chain

const compose = middlewares.reduce((f1, f2) => (...args) => f1(f2(...args)))
// compose好后传入middleware 1 的next就是middleware 2, 传入middleware 2 的next就是middleware3
// 而middleware 3 的next就是调用compose后的传入的原始dispatch
const dispatch = compose((action) => {
  console.log('origin dispatch', action)
})
// 最终执行绑定好后的dispatch,就相当于最终执行每个middleware,每个middleware会传递action参数给原始的dispatch
// redux-thunk 的核心原理就是:检测到传入的action如果是函数类型,就执行这个函数
dispatch({ a: 3 })
// log 如下
middleware 1
middleware 2
middleware 3
origin dispatch {a: 3}
middleware 3 after
middleware 2 after
middleware 1 after

applyMiddleware的本质就是把中间件函数参数先一个一个的绑定好,来增强store 的dispatch的执行

combineReducers

combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  // 收集所有传入的 reducer 函数
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  // 在 dispatch 时会执行 combination 函数,
  // 遍历执行所有 reducer 函数。如果某个 reducer 函数返回了新的 state,就标记hasChanged为true,
  // 所有的 reducer 函数都会被执行一遍
  // hasChanged ? 返回新的state : 返回原来的state

  return function combination(state = {}, action) {

    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。