Redux 高級 -- 源碼分析

createStore.js

createStore

redux 的一切都從 createStore 開始node

const store = redux.createStore(reducer, preloadedState, enhancer);
複製代碼

createStore 能夠接受三個參數:git

  • reducer 必須 {functioin} 計算 state 改變的函數
  • preloadState 可選 {object} 最初的 state
  • enhancer 可選 {function} store 的加強器

若是沒有設置最初的 state,第二個參數也能夠傳 enhancer,也是會正確執行的。github

const store = redux.createStore(reducer, enhancer);
複製代碼

由於源碼裏面有這麼一段:redux

// 若是第二個參數是 function,沒有第三個參數
// 那麼就認爲第二個參數是 enhancer,preloadedState 是 undefined
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
  enhancer = preloadedState
  preloadedState = undefined
}
複製代碼

最終 createStore 返回一個 store 對象:數組

return {
  dispatch,
  subscribe,
  getState,
  replaceReducer,
  // ...
}
複製代碼

接下來咱們一個個來看 store 對象裏的這些成員。app

dispatch

dispatch 負責發出 action:async

store.dispatch({ type: 'ACTION_TYPE' });
複製代碼

dispatch 只接收一個參數,那就是 action,最終返回這個 action。函數

dispatch 只作了兩件事:源碼分析

  • 獲取 reducer 計算的新的 state,並把舊的 state 換成新的 state
  • 調用經過 store.subscribe(listener) 註冊的 listener

源碼:post

function dispatch(action) {
  // ...
  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.')
  }

  try {
    isDispatching = true

    // **核心工做**:調用 reducer 計算出最新的 state
    // oldState + action => newState
    // reducer 在獲取到新的 action 通知後,計算出最新的 state
    currentState = currentReducer(currentState, action)
  } finally {
    isDispatching = false
  }

  // 調用經過 store.subscribe(listener) 註冊的監聽函數
  const listeners = (currentListeners = nextListeners)
  for (let i = 0; i < listeners.length; i++) {
    const listener = listeners[i]
    listener()
  }

  // 原封不動的返回 action
  return action
}
複製代碼

reducer

從上面的源碼:

currentState = currentReducer(currentState, action)
// nextState = reducer(prevState, action)
複製代碼

能夠看出,咱們傳進去的參數 reducer 必須是這樣的:接受 state 和 action,經過一頓計算以後,返回新的 state。至於怎麼計算,就是咱們本身編寫了。

subscribe

註冊事件,每次 dispatch action 的時候都會調用。

subscribe(listener) 接收一個 {function} listener 做爲參數,同時返回一個函數用來註銷原來註冊的時間 listener

function subscribe(listener) {
  //...
  nextListeners.push(listener)

  return function unsubscribe() {
    // ...
    const index = nextListeners.indexOf(listener)
    nextListeners.splice(index, 1)
  }
}

// 註冊
const unsubscribe = store.subscribe(() => { console.log('action dispatched!'); });

// 註銷
unsubscribe();
複製代碼

getState

getState 方法返回最新的 state

function getState() {
  // ...
  return currentState
}
複製代碼

replaceReducer

replaceReducer 接收一個參數 {function} reducer。用傳入的 reducer 替換原來的 reducer

function replaceReducer(nextReducer) {
  // ...
  currentReducer = nextReducer
  dispatch({ type: ActionTypes.REPLACE })
}
複製代碼

createStore 續

接下來咱們繼續回來看看 createStore 裏的邏輯:

function createStore(reducer, preloadedState, enhancer) {
  // ...

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

    return enhancer(createStore)(reducer, preloadedState)
  }

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

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

  dispatch({ type: ActionTypes.INIT })

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

對的,就只有這麼一點邏輯:若是有 enhancer,返回 enhancer 的執行結果,而後 dispatch 一個初始化的 action -- { type: ActionTypes.INIT } 就沒了。

因此這個 enhancer 大有文章,是塊硬骨頭。而 enhancer 咱們在使用的時候知道,是調用 applyMiddleware 生成的,可是 applyMiddleware 裏有個很是重要的函數,必需要先拿出來說一講 -- compose

compose.js

compose 函數接收若干個函數做爲參數,並從右到左組合起來,返回這個組合起來的函數。

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)))
}
複製代碼

舉個例子:

function a(...args) { console.log('a', [...args]); return { a: [...args] }; }
function b(...args) { console.log('b', [...args]); return { b: [...args] }; }
function c(...args) { console.log('c', [...args]); return { c: [...args] }; }
compose(a, b, c);
複製代碼

你們能夠思考下返回的是什麼

(...args) => a(b(c(...args)))
複製代碼

對的,返回的是一個函數,這個函數接收若干個參數 args,而且把這些參數給 c 執行,而後 c 執行的結果再次做爲參數給 b 執行,以此類推。讓咱們來試一下吧:

compose(a, b, c)(1, 2, 3, 4, 5);
// c [1, 2, 3, 4, 5]
// b [{ c: [1, 2, 3, 4, 5] }]
// a [{ b: [{ c: [1, 2, 3, 4, 5]}] }]
複製代碼

applyMiddleware.js

搞懂了 compose,接下來就能夠看這個源碼了:

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
    }
  }
}
複製代碼

原來 applyMiddleware 啥也沒作,就返回了一個函數 createStore => { // ... }。這就是咱們傳到 createStore 裏的 enhancer,它又接收 createStore 函數做爲參數,返回了一個函數 (...args) => { // ... },咱們暫且叫它 executor。

因此上面源碼的這麼一段能夠這樣拆分:

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

// 拆分爲
const enhancer = applyMiddleware(middileware1, middleware2);

const executor = enhancer(createStore);
const result = executor(reducer, preloadedState); // 拆分以後就能夠主要看這一段了
return result;
複製代碼

單獨拎出來看看

function executor(...args) {

  // 經過傳入的參數建立新的 store
  const store = createStore(...args)

  // 默認的 dispath
  let dispatch = () => {
    throw new Error(
      `Dispatching while constructing your middleware is not allowed. ` +
        `Other middleware would not be applied to this dispatch.`
    )
  }

  // 注入給 middleware() 的參數 { getState, dispatch }
  // 前面的文章有講到如何寫 middleware, 它的形式:
  // ({ getState, dispatch }) => next => action => { // ... }
  const middlewareAPI = {
    getState: store.getState,
    dispatch: (...args) => dispatch(...args)
  }

  // 把 middleware 執行並存放到數組裏
  // chain = [ next => action => { // middleware1 }, next => action => { //middleware2 }]
  const chain = middlewares.map(middleware => middleware(middlewareAPI))

  // compose 起來,傳給最後一個 middleware 的參數是 store.dispatch,上面有講過 compose
  dispatch = compose(...chain)(store.dispatch)

  return {
    ...store,

    // 用新的 dispatch 替換最先的 store.dispatch
    dispatch
  }
}
複製代碼

咱們仍是把上面的 dispatch = compose(...chain)(store.dispatch) 拆開來看:

compose(...chain)
// compose(next => action => { console.log('m1', next) }, next => action => { console.log('m2', next) });

// 結果
const executor = (...args) => a(b(...args));

//其中
// a = next => action => { console.log('m1', next) }
// b = next => action => { console.log('m2', next) }

// 而後
dispatch = executor(store.dispatch)

// 結果
dispatch = a(b(store.dispatch))

// 即
(store.dispatch /** m2 next */ => {
  action => {
    console.log('m2', next)
  }
} /** m1 next */ ) => {
  action => {
    console.log('m1', next);
  }
}
複製代碼

總結下來,假設有 3 箇中間件:m1m2m3。這個時候 dispatch 了一個 action。此時的 dispatch 已經不是最原始的 store.dispatch 了。而是通過 compose 過中間件的 dispatch。這個 action 會依次通過 m1, m2, m3 的處理,m1 處理完了以後會調用 next(action),這個 next 就是 m2,其實是調用第二個中間件處理 action,而後依次類推,m2 會調用 next(action),這個 next 就是 m3。由於 m3 是最後一箇中間件,因此 m3 裏的 next 實際上就是 store.dispatch

咱們來用這個例子驗證一下:

const redux = require('redux');

const enhancer = redux.applyMiddleware(
  function({ dispatch }) {
    return function(next) {
      return function(action) {
        console.log('m1', next);
        next(action);
      }
    }
  },
  function({ dispatch }) {
    return function(next) {
      return function(action) {
        console.log('m2', next);
        next(action);
      }
    }
  },
  function({ dispatch }) {
    return function(next) {
      return function(action) {
        console.log('m3', next);
        next(action);
      }
    }
  }
);

const store = redux.createStore(function(state = { test: 'a' }, action) {
  if (action.type === 'TEST') {
    return Object.assign({}, state, { test: 'b' });
  }
  return state;
}, enhancer);

store.dispatch({ type: 'TEST' });
// m1 function (action) {
// console.log('m2', next);
// next(action);
// }
// m2 function (action) {
// console.log('m3', next);
// next(action);
// }
// m3 function dispatch(action) {
// if (!(0, _isPlainObject2['default'])(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;
// }

// var listeners = currentListeners = nextListeners;
// for (var i = 0; i < listeners.length; i++) {
// var listener = listeners[i];
// listener();
// }

// return action;
// }
複製代碼

結果和預料的是同樣的。

代碼地址:Redux 高級 -- 源碼分析,控制檯執行 node ./demo6/index.js 便可

上一篇:Redux 進階 -- 編寫和使用中間件

相關文章
相關標籤/搜索