Redux 相關概念及源碼分析

劃重點:

  • 三個概念:(state, action) => stateredux

    • store
    • action,例如:{type: 'COMPLETE_TODO',index: 1}
    • reducer,只是一些純函數,它接收先前的 state 和 action,並返回新的 state。
  • 三個原則:單一數據源,State 是隻讀的,使用純函數執行修改

概念相關源碼

createStore

首先咱們先看下 createStore 的參數列表及其返回值:app

export default function createStore(reducer, preloadedState, enhancer) {
  // 第一個參數reducer(function類型),preloadedState是初始state,enhancer參見下面解釋
  // 第二,第三參數可爲空,可互換,這裏不列源碼了

  // ...

  // 返回一個包含一大堆函數的對象,具體做用咱們後面看,這裏咱們首先知道它是返回了一個對象
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

Store Enhancers

enhancers 英語譯爲「加強劑」,其實做用也的確如此。用一個表達式來解釋:函數

// f方法對 g 方法作了一些功能加強
h(x) = f(g(x));

例如:this

const g = (t)=>{
  console.log(`do ${t} task`);
}
const f = (func) => {
  console.log('add enhancers');
  return func;
}
const h = f(g);
h('first');

其實 Store Enhancers 也就是這個概念,來看相關源碼:spa

export default 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)
  }
  // ...
}

從源碼中能夠看出兩點:日誌

  1. redux 會把 createStore 函數傳給咱們寫的 enhancer
  2. 同時 reducer,preloadedState參數也會傳給咱們

不知道這樣是否是很清晰了?咱們來寫一個例子,功能相似簡化版的 redux-logger,能夠在每一個dispatch時打印日誌。code

function createLogger(createStore){
  return (reducers,initialState) => {
    const store = createStore(reducers, initialState);
    function dispatch(action){
      console.log(`dispatch an action: ${JSON.stringify(action)}`);
      const res = store.dispatch(action);
      const newState = store.getState();
      console.log(`current state: ${JSON.stringify(newState)}`);
      return res;
    }
    return {...store,dispatch};
  }
}
var store = Redux.createStore(counter,createLogger);

這樣就實現了一個記錄日誌的功能,每次 dispatch 的時候都會記錄。
有些人可能想到了,enhancer權力好像有些大?是的,它能夠加強 store 的行爲,同時也可能破壞它,因此你們寫 enhancers 的時候必定要注意:__不要破壞了Redux的原有工做流__。好比上面例子中這行代碼就保證了 Redux 本來的工做流。對象

const res = store.dispatch(action);

Middleware

可能會有人還不知道 Middleware 的含義,仍是來解釋一下吧,說洋蔥模型可能太文藝了,舉這麼個例子吧,超級瑪麗爲了救公主要經歷 1-1,1-2,1-3,1-4...8-4 關,才能救到公主,這裏的每一關就能夠理解成一個Middleware,只不過如今咱們是遊戲製做人,關卡由咱們來設定。遊戲

若是你看了上面的實例,可能會想 redux-logger 不是一個 Middleware 嗎?和 store enhancers 是什麼關係啊?咱們先來看一下在 redux 中如何定義一個 Middleware,舉官網上的例子:文檔

import { createStore, combineReducers, applyMiddleware } from 'redux'
let todoApp = combineReducers(reducers)
let store = createStore(
  todoApp,
  applyMiddleware(logger, crashReporter)
)

你們再回想上面例子的代碼:

var store = Redux.createStore(counter,createLogger);

是否是很類似呢?沒錯,原理都是一個。這裏調用了 Redux 提供的 applyMiddleware 方法,從而實現了咱們上面的過程。因此,我把這裏的 Middleware 概括成 __一個只能夠擴展 dispatch 方法的 Store Enhancers__。下面列出 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
    }
  }
}

reducer enhancers

顧名思義,reducer的「加強劑」。首先咱們再回到 createStore 中,第一個參數咱們須要定義reducers,不考慮state分割的狀況下,咱們可能會定義成這樣:

function counter(state, action) {
  if (typeof state === 'undefined') {
    return 0
  }

  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

然而這個「模板」是一直從 Redux 文檔中流傳下來的,但若是我寫成這樣呢?

function counter(state,action){
  //我啥也不作
}

實時證實在寫成這樣在 createStore 的時候也能夠經過的,只不過在store.getState()的時候是undefined

因此,我的認爲沒有什麼 reducer enhancers 的概念。

dispatch 的時候是如何調用 reducer 的

其實很簡單,直接看源碼:

function dispatch(action) {
    // 各類判斷
    try {
      isDispatching = true
      // 正常狀況下 currentReducer 就是咱們傳進去的 reducer
      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
  }

上述代碼中,咱們還看到 dispatch 的時候還會調用 listener()listeners 是經過 subscribe 方法來訂閱的,好比一般咱們所作的會把 render 方法添加到這裏。這樣就能夠實現數據變化後從新渲染了。

相關文章
相關標籤/搜索