Redux源碼學習

1.createStore

createStore是使用redux的入口,也是使用redux必須用到的一個方法,其顧名思義就是建立一個可供全局使用的storereact

該方法接受三個參數(reducer, preloadedState, enhance)json

前兩個參數沒必要多說,第三個參數則是用來添加中間件,他是以原始的createStore爲參數,而createStore真正須要的數據爲reducerpreloadedStateredux

createStore.js裏有這麼一行代碼:數組

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

    return enhancer(createStore)(reducer, preloadedState)
  }
複製代碼

行代碼展現了enhancer的調用過程,根據這個調用過程咱們能夠推導出enhancer的函數體的架子應該是這樣子的:bash

function enhancer(createStore) {
    return (reducer,preloadedState) => {
         //邏輯代碼
        .......
    }
 }
在實際應用中,enhancer每每就是redux-thunk以及redux-saga,通常都會配合applyMiddleware一塊兒使用,而applyMiddleware的做用就是將這些enhancer格式化成符合redux要求的enhancer。
複製代碼

createStore返回的是一個對象,咱們一般稱之爲store,store的結構以下:閉包

return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
複製代碼

createStore的返回值爲一個對象,咱們經常使用的爲前三個方法即dispatch, subscribe, getState這三種方法即服務於將數據流以flux的形式進行傳輸,即在對store的change進行監聽,並以訂閱發佈模式執行每一個監聽函數,固然redux在每一個階段都作了異常檢測。app

dispatchaction的作了isPlainObjectundefined的判斷,並在dispatching的時候用一個flag–isDispatching來標誌狀態,保證在同一時段只有一個dispatch函數

subscribe接受listener爲參數,此處的listener是什麼?該函數首先肯定listenerfunction ,而後對isDispatching作了判斷,確保store 的穩定性,而後以一個flag -- isSubscribed 來標誌該listener 的監聽狀態,同時對currentListeners 作了次淺拷貝後將 listener 添加到監聽隊列裏,此時完成了對監聽事件的綁定,以後返回一個該listener取消訂閱的函數,該函數裏面即把 isSubscribed置位,並又對currentListeners作了次淺拷貝後,將 listener 從監聽隊列裏移除。ui

這裏每次在改變監聽隊列前都對監聽隊列作了一次淺拷貝的緣由是在於防止當redux在通知全部訂閱者的時候,此時又有一個新的訂閱者加進來了。若是隻用 currentListeners 的話,當新的訂閱者插進來的時候,就會打亂原有的順序,從而引起一些嚴重的問題。this

getState是redux暴露store的惟一方法,而後該方法內部也只對 isDispatching 作了判斷,避免在 store 改變時被獲取,而後直接返回store,可是不能直接對state賦值,而是經過setState的方法改變state,用來觸發訂閱者的監聽函數。

2.combineReducers

combineReducers函數來把多個reducer 函數合併成一個reducer函數。

其參數爲一個json對象及全部reducer的一個集合,key爲store該reducer對應的屬性,值爲對應的reducer(一個函數)

返回值爲一個合併後的reducer函數

這個函數作了三件事:

1.對參數和參數的key(對應store中的屬性)作了一個淺拷貝

2.對淺拷貝後的參數中每一個reducer作返回值檢測(檢測是否有默認返回值)

3.返回一個合併後的reducer函數,而在返回的函數內部,combineReducers對其也作了異常檢測。異常檢測主要是由 getUnexpectedStateShapeWarningMessage 該方法執行,

getUnexpectedStateShapeWarningMessage接收四個參數 inputState(state)reducers(finalReducers)、action(action)unexpectedKeyCache(unexpectedKeyCache),這裏要說一下unexpectedKeyCache是上一次檢測inputState獲得的其裏面沒有對應的reducer集合裏的異常key的集合。整個邏輯以下:

  1. 前置條件判斷,保證reducers集合不爲{}以及inputState爲簡單對象
  2. 找出inputState裏有的key可是 reducers集合裏沒有key
  3. 若是是替換reducer的action,跳過第四步,不打印異常信息
  4. 將全部異常的key打印出來

再異常檢測定義了一個hasChanged變量用來表示state是否發生變化,遍歷reducers集合,將每一個reducer對應的原state傳入其中,得出其對應的新的state。

3.applyMiddleware和compose

上面講enhance的時候說過,applyMiddleware 返回的就是一個enhanceapplyMiddleware 顧名思義就是一個添加中間件的方法,而中間件添加的最佳時機在於dispatch ,由於 dispatch就是store 改變的發起動做。

中間件極可能不止一個,所以寫成鏈式結構,能夠將其解耦,而第一個傳入的參數則爲dispatch。---compose

下面是一個 applyMiddleware 的邏輯實現。

const applyMiddleware = function (...middlewares) { /*返回一個重寫createStore的方法*/ return function rewriteCreateStoreFunc(oldCreateStore) { /*返回重寫後新的 createStore*/ return function newCreateStore(reducer, initState) { /*1. 生成store*/ const store = oldCreateStore(reducer, initState); /*給每一個 middleware 傳下store,至關於 const logger = loggerMiddleware(store);*/ /* const chain = [exception, time, logger]*/ const chain = middlewares.map(middleware => middleware(store)); let dispatch = store.dispatch; /* 實現 exception(time((logger(dispatch))))*/ chain.reverse().map(middleware => { dispatch = middleware(dispatch); });

/2. 重寫 dispatch/ store.dispatch = dispatch; return store; } } }

applyMiddleware主要就是經過鏈式增強dispatch。實際的js代碼以下:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    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.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
複製代碼

整體的實現邏輯爲:

  1. 經過createStore方法建立出一個store
  2. 定一個dispatch,若是在中間件構造過程當中調用,拋出錯誤提示。即防止在中間件構造過程當中調用dispatch
  3. 定義middlewareAPI,有兩個方法,一個是getState,另外一個是dispatch,將其做爲中間件調用的store的橋接
  4. middlewares調用Array.prototype.map進行改造,存放在chain
  5. 用compose整合chain數組,並賦值給dispatch
  6. 將新的dispatch替換原先的store.dispatch

compose的做用即將全部中間件以鏈式的形式調用。

4.bindActionCreators

bindActionCreators 不多用到,通常只有在 react-redux 的 connect 函數中的mapDispatchToProps中用到。

其做用網上說是他經過閉包,把 dispatchactionCreator 隱藏起來,讓其餘地方感知不到 redux 的存在。可是我仍是不知道這樣作的意義是在於?

bindActionCreators內部針對於三種狀況有三種返回值。

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  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"?`
    )
  }

  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
複製代碼

第一種狀況,當 typeof actionCreators === 'function' 時,

直接返回

return function() {
    return dispatch(actionCreator.apply(this, arguments))
}
複製代碼

即直接返回一個返回值爲 dispatch某個action 的函數的函數。

第二種狀況,當typeof actionCreators !== 'object' || actionCreators === null

一直拋出一個 Error ,指出bindActionCreators 須要的 actionCreator 是一個函數或者對象。

第三種狀況,即默認狀況,這個時候的 actionCreator 是不少個第一種狀況的集合,即 import * as manyActions from '../actions',這裏的 manyActions 就是做爲第三種狀況傳入 boundActionCreatorsactionCreators

這個時候會對actionCreators 作一個遍歷,將每一項都執行一遍第一種狀況的操做。

相關文章
相關標籤/搜索