讀redux源碼總結

redux介紹

redux給咱們暴露了這幾個方法webpack

{
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}

咱們來依次介紹下web

createStore

建立一個store的寫法:redux

let store = createStore(reducer, preloadedState, enhancer);

createStore中的三個參數reducer, preloadedState, enhancer,後面兩個是可選參數,
當咱們只傳兩個參數,而且第二個參數是函數時,例如:數組

let store = createStore(reducer, enhancer);

經過閱讀源碼這段數據結構

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

咱們知道,上面的createStore會被改寫成這樣:閉包

createStore(educer, undefined, enhancer)

再經過閱讀源碼這段:app

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, undefined)

因此實例化一個createStore方法,如下兩種是等價的:ide

第一種
const store = createStore(reducers, applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware));


第二種
const store = applyMiddleware(routeMiddleware, sagaMiddleware, postRedirectMiddleware, pageSizeMiddleware)(createStore)(reducers);

最後,返回的store對象提供瞭如下幾個方法給咱們使用模塊化

dispatch,
subscribe,
getState,
replaceReducer,

getState用來獲取currentState,也就是總state值

subscribe註冊多個監聽函數,這些函數在開發者調用dispatch時會依次執行

dispatch的入參是一個對象action,直接會執行reducer(action)方法,而且批量執行subscribe監聽的內容,dispatch執行結束後會返回一個action

replaceReducer替換當前的reducer,這個方法在異步的單應用中可使用利用起來,例如,咱們不想一次性將全部的reducer交給createStore初始化,而是當異步獲取某個頁面時,再將這個頁面的reducer加上以前的舊reducer,經過replaceReducer方法來替換成最新的。這樣作的好處是,devtools裏的redux工具不會展現那麼多的信息,只會展現訪問過頁面的信息,更有利於咱們的開發,固然了因爲reducer的減小,store的體積也會變小,頁面執行速度更快。

combineReducers

源碼

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }

  let shapeAssertionError
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    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
    }
    return hasChanged ? nextState : state
  }
}

一般咱們會這樣使用

combineReducers({
    a:reducer1,
    b:reducer2
})

reducer1是一個函數,而combineReducers返回的任然是一個函數,只不過將每一個reducer都遍歷了一遍,最後返回的數據結構爲

{
    a:state1,
    b:state2
}

而若是咱們把a跟b替換成了一個惟一的路徑path,這個path跟項目中每一個頁面的文件夾對應起來,例如:

combineReducers({
    'component/page1/info/':reducer1,
    'component/page2/user/':reducer2
})

而在取state數據的時候這樣來取
state['component/page1/info/']是否是就能夠經過文件夾路徑實現模塊化了,固然了咱們離模塊化還差一小步,就是不用人工來寫路徑path,而是交給構建工具(webpack的loader)來遍歷完成。

compose

源碼

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

我的以爲compose(a,b,c)(d)後的代碼就是a(b(c(d))),compose後返回的任然是一個函數,能夠接受參數,compose在後面介紹的代碼中有所涉及。

applyMiddleware

源碼

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.`
      )
    }
    let chain = []

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

    return {
      ...store,
      dispatch
    }
  }
}

建立一個store的方法:

let store = applyMiddleware(middleware1,middleware2,middleware3)(createStore)(reducers)

中間件middleware1的代碼

function middleware1({ getState }) {
  return (next) => (action) => {
    console.log('will dispatch', action)
    let returnValue = next(action)
    console.log('state after dispatch', getState())
    return returnValue
  }
}

applyMiddleware傳入的參數爲多箇中間件,中間件的做用是在執行reducers中的switch以前,先執行中間件中的代碼。

對應源碼中的參數來說的話,
實例參數middleware一、middleware2就至關於源碼入參...middlewares,
實例參數createStore對應着源碼入參createStore,
實例參數reducers對應着源碼入參...args。

源碼中的

const store = createStore(...args)

獲取store對象供後面使用,middlewareAPI裏的getState對應着store對象裏的getState,getState()方法能夠獲取currentState,也就是redux對象樹的總數據。

chain = middlewares.map(middleware => middleware(middlewareAPI))

middlewareAPI提供了getState這個方法,供中間件獲取currenState,這時候chain獲取的數組是一個返回值爲函數的函數。

下面這行代碼比較精彩

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

首先將最原始的store.dispatch方法做爲入參,

dispatch(action)

就至關於
compose(...chain)(store.dispatch)(action),

同時也至關於
middleware1(middleware2(middleware3((store.dispatch))))(action),

固然這裏的middleware1是已經只剩下閉包內的兩層函數不是原來的三層函數體。

最早執行的是middleware1,
返回了next(action),

也就至關於
middleware2(middleware3((store.dispatch)))(action),

執行完後返回next(action)
就至關於middleware3((store.dispatch))(action),

執行完後返回next(action)
就至關於store.dispatch(action)

整個過程咱們已經弄清楚了,applyMiddleware中間件的執行過程就是不斷的next(action),
而只有最後的next纔是執行dispatch,以前的next只表明的傳遞其餘中間件,dispatch方法只在最後一箇中間件裏執行了一次。

我的以爲這個地方結合了compose以後寫的比較精彩,並且設計的很是巧妙。

相關文章
相關標籤/搜索