Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redux原理解析 #18

Open
mbaxszy7 opened this issue Jul 1, 2020 · 0 comments
Open

Redux原理解析 #18

mbaxszy7 opened this issue Jul 1, 2020 · 0 comments
Labels

Comments

@mbaxszy7
Copy link
Owner

mbaxszy7 commented Jul 1, 2020

Redux概念

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

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
  }
}
@mbaxszy7 mbaxszy7 added the redux label Jul 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant