redux源碼解析

1.前言

關於redux的基本概念和工作流如何進行的這裏就不進行過多概述了,可以查看相關文檔去了解。
流程圖鏈接

2.redux源碼結構

以下是redux的源碼結構圖,主要的就是以下幾個文件組成,我們接下來按順序進行介紹其中原理和實現過程。

3.createStore.js

首先了解下createStore.js。通過調用createStore創建唯一的store,store中暴露出getState,dispatch,subscribe,replaceReducer這幾個方法。通常我們用到的主要是前三個方法,這裏作爲主要介紹內容。如下是createStore的主要內容:

export function createStore(reducer, preloadedState, enhancer) {
  /**
   * 以下的判斷都是對傳入的參數進行驗證
   */
  if(
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function') 
  ) {
    throw new Error('只能傳遞一個enhancer到createStore()中')
  }

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

  if(typeof enhancer !== 'undefined') {
    if(typeof enhancer !== 'function') {
      throw new Error('enhancer應該爲一個函數')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if(typeof reducer !== 'function') {
    throw new Error('reducer應該爲一個函數')
  }

  /**
   * 初始化參數
   */
  let currentReducer = reducer //初始化reducer
  let currentState = preloadedState //初始化state
  let currentListeners = [] //初始化subscribe監聽函數數組
  let nextListeners = currentListeners 
  let isDispatching = false

  /**
   * 複製一份currentListeners,爲了防止在dispatch的時候
   * 調用subscribe和unsubscribe時候發生錯誤
   */
  function ensureCanMutateNextListeners() {
    if(nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * 獲取當前的state
   */
  function getState() {
    if(isDispatching) {
      throw new Error('不可以在isDispatching的時候調用getState')
    }
    return currentState
  }

  /** 
   * 訂閱監聽事件,觸發dispatch後執行
  */
  function subscribe(listener) {
    if(typeof listener != 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if(isDispatching) {
      throw new Error('isDispatching的時候無法調用')
    }

    let isSubscribed = true
    ensureCanMutateNextListeners() 
    nextListeners.push(listener)

    return function unsubscribe() {
      if(!isSubscribed) { //正在解除監聽事件的時候不向下執行
        return
      }
      if(isDispatching) {
        throw new Error('正在dispatch的時候不給執行')
      }
      isSubscribed = false 
      ensureCanMutateNextListeners() 
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index)
    }
  }

  /**
   * 執行好dispatch循環調用每個subscribe的函數
   */
  function dispatch() {
    //關於驗證的代碼就不寫了
    const listeners = (currentListeners = nextListeners)
    for(let i=0; i<listeners.length; i++) {
      listeners[i]()
    }
    return action
  }

  /**
   * 替換當前的reducer然後重新初始化一次dispatch
   */
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({type: '@INITACTION'})
  }

  //初始化執行dispatch
  dispatch({type: '@INITACTION'})
}

4. combineReducers.js

combineReducers,它接收多個reducer函數,並整合,歸一化成一個rootReducer。其返回值rootReducer將會成爲createStore的參數,完成store的創建。
combineReducers只接收一個參數,這個參數闡述了不同reducer函數和頁面狀態數據樹不同部分的映射匹配關係。

const combineReducers = (reducers) => {
    return (state={}, action) => {
        Object.keys(reducers).reduce((nextState, key) => {
            nextState[key] = reducers[key](state[key], action)
            return nextState
        }, {})
    }
}

5. applyMiddleware.js

可以通過此方法給redux在觸發action到reducer的過程中增加一箇中間環節。applyMiddleware返回的內容我們稱爲enhancer。這個是createStore方法的最後一個參數,並且是可選的。
在redux源碼中涉及中間件的腳本有applyMiddleware.js、createStore.js、compose.js。那麼applyMiddleware(...middlewares)中會發生什麼事情。
在createStore.js中有一段源碼如下:

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

顧名思義,applyMiddleware就是對各個需要的中間件進行糅合,並作爲createStore的第二個或者第三個參數傳入。用於增強store。源碼如下:

const combineReducers = (reducers) => {
    return (state = {}, action) => {
        return Object.keys(reducers).reduce((nextState, key) => {
            nextState[key] = reducers[key](state[key], action)
            return nextState
        }, {})
    }
}

export default function applyMiddleware(...middlewares) {
    return (next) => {
        return (reducer, initialState) => {
            var store = next(reducer, initialState)
            var dispatch = store.dispatch
            var chain = []

            //包裝一下store的getState和dispatch方法
            //是第三方中間件需要使用的參數
            
            var middlewareAPI = {
                getState: store.getState,
                dispatch: (action) => dispatch(action)
            }
            //每個中間件也是一個高度柯里化的函數,它接收middlewareAPI參數後的第一次返回結果並存儲到chain數組中
            //chain數組中每一項都是對dispatch的增強,並進行控制權轉移。
            chain = middlewares.map(middleware => middleware(middlewareAPI))
            //這裏的dispatch函數就是增強後的dispatch,因此compose方法接收了chain數組和原始dispatch方法。
            dispatch = compose(...chain, store.dispatch)
            return {
                ...store,
                dispatch
            }
        }
    }
}

export default function compose(...funcs) {
    if(funcs.length === 0) {
        return arg => arg
    }
    if(funcs.length === 1) {
        return funcs[0]
    }
    return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

6. compose.js

這個方法在applymiddleware中介紹了,可以在上面看到。

7.bindActionCreators.js

這個模塊涉及的內容較少,我們直接去看源碼:

function bindActionCreator(actionCreator, dispatch) {
    //這個函數主要作用就是返回一個函數,當我們調用返回的這個函數的時候
    //會自動的dispatch對應的action
    return function() {
        return dispatch(actionCreator.apply(this, args))
    }
}
/**
    參數說明: 
        actionCreators: action create函數,可以是一個單函數,也可以是一個對象,這個對象的所有元素都是action create函數
        dispatch: store.dispatch方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
  // 如果actionCreators是一個函數的話,就調用bindActionCreator方法對action create函數和dispatch進行綁定
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  // actionCreators必須是函數或者對象中的一種,且不能是null
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
      `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }

  // 獲取所有action create函數的名字
  const keys = Object.keys(actionCreators)
  // 保存dispatch和action create函數進行綁定之後的集合
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    // 排除值不是函數的action create
    if (typeof actionCreator === 'function') {
      // 進行綁定
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  // 返回綁定之後的對象
  /**
      boundActionCreators的基本形式就是
      {
      actionCreator: function() {dispatch(actionCreator.apply(this, arguments))}
      }
  */
  return boundActionCreators
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章