redux 源碼全方位剖析

版本:v4.0.0html

後續內容更新,請前往:我的博客,歡迎一塊兒交流。前端

前言

受2014年Facebook的Flux架構模式以及函數式編程語言Elm啓發,Dan Abramov在2015年建立了 Redux。很快,Redux因其體小精悍(只有2kB)且沒有任何依賴短期內成爲最熱門的前端架構。webpack

Redux 是可預測的狀態管理框架,它很好的解決多交互,多數據源的訴求。Redux 設計之初,做者就嚴格遵循三個設計理念原則:
單一數據源:整個應用的 state 都被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。store 能夠看作是數據存儲的一個容器,在這個容器裏面,只會維護惟一一個 state tree。store 會給定4種基礎操做API:dispatch(action),getState(),replaceReducer(nextReducer),subscribe(listener)。根據單一數據源原則,全部數據會經過store.getState()方法調用獲取。
state只讀:根據 state 只讀原則,數據變動會經過 store,dispatch(action) 方法,Action 能夠理解爲變動數據的信息載體,type 是變動數據的惟一標誌,payload 是用來攜帶須要變動的數據,格式大體爲:const action = { type: 'xxx', payload: 'yyy' };Reducer 是個純函數,負責根據 action.type 獲取須要變動的數據,而後計算 state 數值。格式爲:reducer: prevState => action => newState
使用純函數變動state值:Reducer 只是一些純函數,它接收先前的 state 和 action,並返回新的 state。git

正常的一個同步數據流爲:view 層觸發 actionCreator,actionCreator 經過 store.dispatch(action) 方法變動 reducer。可是面對多種多樣的業務場景,同步數據流方式顯然沒法知足。對於改變reducer的異步數據操做,就須要用到中間件的概念,如圖所示:github

clipboard.png

在開始以前咱們先作如下幾點約定:
第一:目前分析的版本是 redux 的最新版本 4.0.0;
第二:我儘量站在我本身的角度去剖析,固然我會借鑑社區比較優秀的文章,歡迎與你們一塊兒交換意見,努力寫好該 redux 源碼系列;
第三:若是有幸您讀到該 redux 源碼系列,感受寫得還行,還望收藏、分享或打賞。web

源碼結構

Redux 的源碼結構很簡單,源碼都在 src 目錄下,其目錄結構以下:編程

src
├── utils ---------------------------------------- 工具函數
├── applyMiddleware.js --------------------------- 加載 middleware
├── bindActionCreators.js ------------------------ 生成將 action creator 包裹在 dispatch 裏的函數
├── combineReducers.js --------------------------- 合併 reducer 函數
├── compose.js ----------------------------------- 組合函數
├── createStore.js ------------------------------- 建立一個 Redux store 來儲存應用中全部的 state
├── index.js ------------------------------------- 入口 js

源碼入口

index.js 是整個代碼的入口,其代碼以下:redux

import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

function isCrushed() {}

if (
    process.env.NODE_ENV !== 'production' &&
    typeof isCrushed.name === 'string' &&
    isCrushed.name !== 'isCrushed'
) {
  warning(
    'You are currently using minified code outside of NODE_ENV === "production". ' +
      'This means that you are running a slower development build of Redux. ' +
      'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
      'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
      'to ensure you have the correct code for your production build.'
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}

入口代碼很簡單,首先isCrushed函數主要是爲了驗證在非生產環境下Redux是否被壓縮?若是被壓縮了,isCrushed.name !== 'isCrushed' 就等於 true,這樣就會給開發者一個warn提示。最後暴露createStorecombineReducersbindActionCreatorsapplyMiddlewarecompose 這幾個接口給開發者使用,接下來咱們逐一解析這幾個 API。api

createStore.js

createStore.js 是 Redux 最重要的一個 API ,它負責建立一個 Redux store 來儲存應用中全部的 state,整個應用中應有且僅有一個 store。如今咱們來看一下 createStore 源代碼:數組

import $$observable from 'symbol-observable'

// 私有 action
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'

export default function createStore(reducer, preloadedState, enhancer) {

    // 判斷接受的參數個數,來指定 reducer、preloadedState 和 enhancer
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        enhancer = preloadedState
        preloadedState = undefined
    }

    // 若是 enhancer 存在且是個合法的函數,就調用 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.')
    }
    // 儲存當前的 currentReducer
    let currentReducer = reducer
    // 儲存當前的狀態
    let currentState = preloadedState
    // 儲存當前的監聽函數列表
    let currentListeners = []
    // 儲存下一個監聽函數列表
    let nextListeners = currentListeners
    let isDispatching = false

    // 這個函數能夠根據當前監聽函數的列表生成新的下一個監聽函數列表引用
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
            nextListeners = currentListeners.slice()
        }
    }

  // 讀取由 store 管理的狀態樹
  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

  function subscribe(listener) {
    // 判斷傳入的參數是否爲函數
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // 判斷 action 是否有 type{必須} 屬性
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    // 若是正在 dispatch 則拋出錯誤
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    // 對拋出 error 的兼容,可是不管如何都會繼續執行 isDispatching = false 的操做
    try {
      isDispatching = true
      // 使用 currentReducer 來操做傳入 當前狀態和 action,放回處理後的狀態
      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
  }

  // 判斷參數是不是函數類型
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  function observable() {
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      }
    }
  }

  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

這裏咱們首先要講一下ActionTypes對象,它是 Redux 的私有 action,不容許外界觸發,用來初始化 store 的狀態樹和改變 reducers 後初始化 store 的狀態樹。接下來咱們從不一樣角度着重來說一下 createStore 函數:

參數

它能夠接受三個參數:reducer、preloadedState、enhancer:
reducer:函數,返回下一個狀態,接受兩個參數:當前狀態 和 觸發的 action;
preloadedState:它是 state 的初始值,能夠隨意指定,好比服務端渲染的初始狀態,可是若是使用 combineReducers 來生成 reducer,那必須保持狀態對象的 key 和 combineReducers 中的 key 相對應,另外實際上它並不只僅是扮演着一個 initialState 的角色,若是咱們第二個參數是函數類型,createStore 會認爲咱們忽略了 preloadedState 而傳入了一個enhancer;
enhancer:可選參數,一個組合 store creator 的高階函數,能夠翻譯成 store 的加強器,顧名思義,就是加強 store 的功能。通常指定爲第三方的中間件,時間旅行,持久化等等,返回一個新的強化過的 store creator,這個函數一般用 Redux 提供的 applyMiddleware 函數來生成。

根據傳入參數的個數和類型,判斷 reducer、preloadedState、enhancer。

返回值

調用完函數的返回值:dispatch、subscribe、getState、replaceReducer 和 [$$observable],這就是咱們開發中主要使用的幾個接口。

enhancer

若是enhancer參數存在且是個合法的函數,那麼就調用enhancer函數。enhancer其實是一個高階函數,它的參數是建立store的函數createStore,返回值是一個能夠建立功能更增強大的store的函數(enhanced store creator),這和 React 中的高階組件的概念很類似。store enhancer 函數的結構通常以下:

function enhancerCreator() {
  return createStore => (...args) => {
    // do something based old store
    // return a new enhanced store
  }
}

注意,enhancerCreator是用於建立enhancer store的函數,也就是說enhancerCreator的執行結果纔是一個enhancer store...args參數表明建立store所需的參數,也就是createStore接收的參數,實際上就是(reducer, [preloadedState], [enhancer])

如今,咱們來建立一個enhancer store,用於輸出發送的action的信息和state的變化:

// logging.js(store enhancer)
export default function logging() {
  return createStore => (reducer, initialState, enhancer) => {
    const store = createStore(reducer, initialState, enhancer)
    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}
  }
}

logging()改變了store dispatch的默認行爲,在每次發送action先後,都會輸出日誌信息,而後在建立store上,使用logging()這個store enhancer:

// store.js
import { createStore, combineReducers } from 'redux';
import * as reducer from '../reducer';
import logging from '../logging';

//建立一個 Redux store 來以存放應用中全部的 state,應用中應有且僅有一個 store。

var store = createStore(
    combineReducers(reducer),
    logging()
);

export default store;

getState

// 讀取由 store 管理的狀態樹
function getState() {
  if (isDispatching) {
    throw new Error(
      'You may not call store.getState() while the reducer is executing. ' +
        'The reducer has already received the state as an argument. ' +
        'Pass it down from the top reducer instead of reading it from the store.'
    )
  }

  return currentState
}

這個函數能夠獲取當前的狀態,createStore 中的 currentState 儲存當前的狀態樹,這是一個閉包,這個參數會持久存在,而且全部的操做狀態都是改變這個引用,getState 函數返回當前的 currentState。

subscribe

function subscribe(listener) {
  // 判斷傳入的參數是否爲函數
  if (typeof listener !== 'function') {
    throw new Error('Expected the listener to be a function.')
  }

  if (isDispatching) {
    throw new Error(
      'You may not call store.subscribe() while the reducer is executing. ' +
        'If you would like to be notified after the store has been updated, subscribe from a ' +
        'component and invoke store.getState() in the callback to access the latest state. ' +
        'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
    )
  }

  let isSubscribed = true

  ensureCanMutateNextListeners()
  nextListeners.push(listener)

  return function unsubscribe() {
    if (!isSubscribed) {
      return
    }

    if (isDispatching) {
      throw new Error(
        'You may not unsubscribe from a store listener while the reducer is executing. ' +
          'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
      )
    }

    isSubscribed = false

    ensureCanMutateNextListeners()
    const index = nextListeners.indexOf(listener)
    nextListeners.splice(index, 1)
  }
}

這個函數能夠給 store 的狀態添加訂閱監聽函數,一旦調用dispatch,全部的監聽函數就會執行;nextListeners就是儲存當前監聽函數的列表,調用subscribe,傳入一個函數做爲參數,那麼就會給nextListeners列表push這個函數;同時調用subscribe函數會返回一個unsubscribe函數,用來解綁當前傳入的函數,同時在subscribe函數定義了一個isSubscribed標誌變量來判斷當前的訂閱是否已經被解綁,解綁的操做就是從nextListeners列表中刪除當前的監聽函數。

dispatch

function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // 判斷 action 是否有 type{必須} 屬性
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    // 若是正在 dispatch 則拋出錯誤
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    // 對拋出 error 的兼容,可是不管如何都會繼續執行 isDispatching = false 的操做
    try {
      isDispatching = true
      // 使用 currentReducer 來操做傳入 當前狀態和 action,放回處理後的狀態
      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
  }

這個函數是用來觸發狀態改變的,它接受一個 action 對象做爲參數,而後 reducer 根據 action 的屬性以及當前 store 的狀態來生成一個新的狀態,賦予當前狀態,改變 store 的狀態;即currentState = currentReducer(currentState, action);這裏的currentReducer是一個函數,它接受兩個參數:當前狀態 和 action,而後返回計算出來的新的狀態;而後遍歷nextListeners列表,調用每一個監聽函數。

replaceReducer

// 判斷參數是不是函數類型
function replaceReducer(nextReducer) {
  if (typeof nextReducer !== 'function') {
    throw new Error('Expected the nextReducer to be a function.')
  }

  currentReducer = nextReducer
  dispatch({ type: ActionTypes.REPLACE })
}

這個函數能夠替換 store 當前的 reducer 函數,首先直接用currentReducer = nextReducer替換;而後dispatch({ type: ActionTypes.INIT }),用來初始化替換後 reducer 生成的初始化狀態而且賦予 store 的狀態。

observable

function observable() {
  const outerSubscribe = subscribe
  return {
    subscribe(observer) {
      if (typeof observer !== 'object' || observer === null) {
        throw new TypeError('Expected the observer to be an object.')
      }

      function observeState() {
        if (observer.next) {
          observer.next(getState())
        }
      }

      observeState()
      const unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },

    [$$observable]() {
      return this
    }
  }
}

對於這個函數,是不直接暴露給開發者的,它提供了給其餘觀察者模式/響應式庫的交互操做。

初始化 store 的狀態

最後執行dispatch({ type: ActionTypes.INIT }),用來根據 reducer 初始化 store 的狀態。

compose.js

compose能夠接受一組函數參數,而後從右到左來組合多個函數(這是函數式編程中的方法),最後返回一個組合函數。如今咱們來看一下 compose 源代碼:

/**
 * For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
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其做用是把一系列的函數,組裝生成一個新的函數,而且從後到前,後面參數的執行結果做爲其前一個的參數,當須要把多個 store 加強器 依次執行的時候,須要用到它。

參數

(...funcs):須要合成的多個函數。每一個函數都接收一個函數做爲參數,而後返回一個函數。

返回值

(Function):從右到左把接收到的函數合成後的最終函數。

applyMiddleware.js

It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.

這是 redux 做者 Dan 對 middleware 的描述,middleware 提供了一個分類處理 action 的機會,在 middleware 中咱們能夠檢閱每個流過的 action,挑選出特定類型的 action 進行相應操做,給咱們改變 action 的機會。
Redux middleware 的設計十分特殊,是一個層層包裹的匿名函數,實際上這是函數式編程中的柯里化,一種使用匿名單參數函數來實現多參數函數的方法,柯里化的 middleware 結構好處在於:
一:易串聯,柯里化函數具備延遲執行的特性,經過不斷柯里化造成的 middleware 能夠累積參數,配合組合的方式,很容易造成 pipeline 來處理數據流。
二:共享 store,在 applyMiddleware 執行過程當中,store 仍是舊的,可是由於閉包的存在,applyMiddleware 完成後,全部的 middlewares 內部拿到的 store 是最新且相同的。

redux 提供了 applyMiddleware 這個 api 來加載 middleware。如今咱們來看一下 applyMiddleware 源代碼:

import compose from './compose'

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.`
      )
    }
    // 暴漏 getState 和 dispatch 供第三方中間件使用
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    // middlewareAPI 做爲每一個 middleware 的參數依次執行一遍,最終返回函數組成的數組
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 用 compose 組合函數生成新的 dispatch
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

由上咱們能夠發現 applyMiddleware 的結構也是一個多層柯里化的函數,藉助 compose,applyMiddleware 能夠用來和其餘插件一塊兒增強 createStore 函數。

參數

咱們在 createStore 小節中其實就用說起過 applyMiddleware:

// 若是 enhancer 存在且是個合法的函數,就調用 enhancer,不然拋出錯誤提示
if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')
  }

  return enhancer(createStore)(reducer, preloadedState)
}

這裏 enhancer 其實就等於 applyMiddleware(mid1, mid2, mid3, ...),所以咱們建立一個 store 實際上就變成以下方式了:

applyMiddleware(mid1, mid2, mid3, ...)(createStore)(reducer, preloadedState);

由上述代碼可知 applyMiddleware 陸續能夠接受四個參數:
[md1, mid2, mid3, ...]: middlewares 數組;
createStore:Redux 原生的 createStore;
reducer:函數,返回下一個狀態;
preloadedState:state 的初始值。
接下來,咱們看一下 applyMiddleware 用這些參數都作了什麼?

const store = createStore(...args)
const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}

const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

applyMiddleware 利用 createStore 和 (reducer, preloadedState) 建立了一個 store,而後 store 的 getState 方法和 dispatch 方法又分別賦值給 middlewareAPI 變量,緊接着用 middlewareAPI 做爲每一個 middleware 的參數依次執行一遍,執行完後,最終得到數組 chain:[f1, f2, ..., fn] 交給組合函數 compose 處理。compose 能夠接受一組函數參數,而後從右到左來組合多個函數(這是函數式編程中的方法),最後返回一個組合函數,例如:

// 調用
dispatch = compose(f, g, h)(store.dispatch)
// 返回
dispatch = f(g(h(store.dispatch)))

這樣經過調用新的 dispatch,每一個 middleware 的代碼就能夠依次執行了。

返回值

store:原來的store;
dispatch:改變後的dispatch。

combineReducers.js

Reducer 是管理 state 的一個模塊,它主要作的事情就是當項目初始化時,返回 initalState,當用戶用操做時,它會根據 action 進行相應地更新。須要注意的是它是一個純函數,換言之,它不會改變傳入的 state。如今咱們來看一下 combineReducers 源碼(源碼有刪減,刪除了一些驗證代碼):

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'

export default function combineReducers(reducers) {
  // 根據 reducers 生成最終合法的 finalReducers:value 爲 函數
  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
  }

  // 返回最終生成的 reducer
  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
  }
}

該函數最終返回 combination 函數,它就是真正 createStore 函數的 reducer,接受一個初始化狀態和一個 action 參數;該函數每次調用大體執行如下幾個操做:
一、for (let i = 0; i < finalReducerKeys.length; i++) { ... }:遍歷 finalReducer(有效的 reducer 列表);
二、var previousStateForKey = state[key]:當前遍歷項的以前狀態,看到這裏就應該明白傳入的 reducers 組合爲何 key 要和 store 裏面的 state 的 key 相對應了;
三、var nextStateForKey = reducer(previousStateForKey, action):當前遍歷項的下一個狀態;
四、nextState[key] = nextStateForKey:將 當前遍歷項的下一個狀態添加到 nextState;
五、hasChanged = hasChanged || nextStateForKey !== previousStateForKey:判斷狀態是否改變;
六、return hasChanged ? nextState : state:若是沒有改變就返回原有狀態,若是改變了就返回新生成的狀態對象。

參數

reducers (Object): 一個對象,它的值(value)對應不一樣的 reducer 函數,這些 reducer 函數後面會被合併成一個。

返回值

(Function):它是真正 createStore 函數的 reducer,接受一個初始化狀態和一個 action 參數;每次調用的時候會去遍歷 finalReducer(有效的 reducer 列表),而後調用列表中每一個 reducer,最終構造一個與 reducers 對象結構相同的 state 對象。

bindActionCreators.js

Redux 中的 bindActionCreators 是經過 dispatch 將 action 包裹起來,這樣就能夠經過 bindActionCreators 建立方法調用 dispatch(action)。如今咱們來看一下 bindActionCreators 源代碼:

function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  // 若是是一個函數,直接返回一個 bindActionCreator 函數,即調用 dispatch 觸發 action
  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"?`
    )
  }
  // 遍歷對象,而後設置每一個遍歷項的 actionCreator 生成函數,最後返回這個對象
  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
}

由此能夠看出 bindActionCreators 的實現邏輯比較簡單:
1、判斷傳入的參數是不是 object,若是是函數,就直接返回一個將 action creator 包裹在 dispatch 裏的函數。
2、若是是object,就根據相應的key,生成相應的將 action creator 包裹在 dispatch 裏的函數。

爲了方便理解,咱們用一個 TODO 例子說明下:

// actions/todo.js
export function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  }
}

export function removeTodo(id) {
  return {
    type: 'REMOVE_TODO',
    id
  }
}

咱們 import 後會獲得以下對象:

{
   addTodo : text => 
    { 
      type: 'ADD_TODO',
      text
    },
   removeTodo : id => {
      type: 'REMOVE_TODO',
      id
    }
}

通過 bindActionCreator 就會變成 key 相同,值爲用 dispatch 將 action creator 包裹起來的函數的對象:

{
   addTodo : text => dispatch(addTodo('text'));
   removeTodo : id => dispatch(removeTodo('id'));
}

由此咱們發現能夠經過 bindActionCreators 建立方法直接調用 dispatch(action)。

參數

它能夠接收兩個參數:actionCreators、dispatch
actionCretors:能夠是一個對象,也能夠是一個單個函數
dispatch:dispatch 函數,從 store 實例中獲取,用於包裹 action creator

若是隻是傳入一個 function,返回的也是一個 function,例如:

// actions/todo.js
export const toggleTodo = (id) => {
  return {
      type: 'TOGGLE_TODO',
      id
  };
};

通過 bindActionCreator:

const boundActionCreators = bindActionCreators(toggleTodo, dispatch);

因爲 bindActionCreators 第一個參數是一個函數,結果就會變爲:

const boundActionCreators  = (id) => dispatch(toggleTodo(id));

返回值

單個函數,或者是一個對象。

總結

經過閱讀 Redux 的源碼,咱們發現 Redux 設計的實在是太精妙了,徹底函數式編程,依賴少,耦合低。

相關文章
相關標籤/搜索