redux原始碼解析

Kevin .ᘜ發表於2018-11-30

前言

相信很多同學跟我一樣對目前比較流行的前端庫的原理和實現 很感興趣,比如(react, vue, webpack, redux, react-redux, react-router...等等等), 但是一開始閱讀原始碼的時候難免有些不順手, 本文以redux的原始碼解讀為例,為你提供一些解讀原始碼的思路,本文為我解讀redux原始碼的全過程,其中也會有一些彎路, 不過還是分享給大家,畢竟本文倡導重在閱讀過程。

話不多說,我們直接開始手擼redux

github上找到redux,clone到本地

  目前最新版本: 4.0.1
複製程式碼

開啟專案, 我的習慣是先瞄一眼package,看一下相關依賴,有沒有peerDependencies等

redux原始碼解析
現在開上去沒什麼值得看的, 瞭解一下用rollup打包即可,然後看一下資料夾, 結構比較常規,我們直接看src,並沒有太多的檔案, 怎麼樣?是不是有了信心?

redux原始碼解析
個人覺得再看別人的程式碼時debugger是一個很好的東西, 可以檢視執行順序提高效率。

好的,那我們就把redux拿出來,可以debugger就方便多了。

想直接擼碼的同學直接跳過這一步,看後面

先用一個你比較熟悉的腳手架快速搭建一個react工程(因為後續會解析react-redux,所以直接用react工程),比如我選擇create-react-app

  create-react-app redux-source-analyse
複製程式碼

把redux中的src改名為redux放在redux-source-analyse專案的src下, 接下來由於作者考慮到閱讀react-redux的時候也需要有一個這樣的專案,直接把redux官網的todoList專案考到了該專案中,開啟專案目錄是這樣的(其實redux原始碼不需要使用這個todolist)

redux原始碼解析
不想這麼麻煩的同學可以直接clone這個專案裡面註釋很詳細, 建議結合此專案來看本文。

我們正式開始擼原始碼

根據常識,最開始我們當然選擇看index.js檔案

redux原始碼解析

嘿嘿,很簡單吧, 看下引用,沒有外部的包,然後返回了一個物件, 這個物件當然就是我們引用的redux啦,

isCrushed.name !=='isCrushed'用來判斷是否壓縮過,如果不是production環境且壓縮了,給出warning
複製程式碼

接下來我們看比較核心的createStore.js

先來看看他是怎麼用的, 是function, 引數 initState, applyMiddleware可選

 const store = createStore(rootReducer, initState, applyMiddleware(logger));
複製程式碼

接著,過一下大體都是什麼東西

redux原始碼解析
直接看程式碼,對應的解釋以註釋,作者是先從上向下看的,先看引數規範的部分(... throw new Error('....') ...) 接著看一下function,這裡推薦先看redux暴露出來的api,比如getState,subscribe, dispatch, 遇到相關的function直接進去看一下, 哪怕是utils裡面的輔助函式(當然因為redux的輔助函式比較簡單,如果閱讀其他庫,複雜的地方可以先跳過,不過要搞清楚函式的功能),閱讀不太順利,需要自己debugger的同學下載他

基本閱讀步驟 (... throw new Error('....') ...)-> let xxx -> getState -> subscribe -> dispatch -> replaceReducer -> observable

import $$observable from 'symbol-observable'

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

// 先看這裡, 就是我們呼叫的createStore function了
export default function createStore(reducer, preloadedState, enhancer) {
  // 如果 preloadedState和enhancer都為function,不支援,throw new Error
  // 我們都知道[initState]為object, [enhancer]為function

  if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function'
    )
  }
  // preloadedState為function enhancer為undefined的時候說明initState沒有初始化, 但是有middleware
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState // 把 preloadedState 賦值給 enhancer
    preloadedState = undefined // preloadedState賦值undeifined
  }

  // debugger
  // 如果引數enhancer存在
  if (typeof enhancer !== 'undefined') {
    // 如果enhancer存在,那他必須是個function, 否則throw Error哈
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    /**
     * 傳入符合引數型別的引數,就可以執行 enhancer, 
     * 但是這個return深深的吸引了我, 因為說明有applyMiddleware的時候後面的都不用看了 ??? 當然不可能
     * 可是applyMiddleware其實是必用項,所以猜想一下applyMiddleware強化store之後會enhancer賦值undefined,再次呼叫createStore
     * 上下打個debugger看一下執行順序(debugger位置以註釋),果然不出所料
     * 好了, 假設我們還不知道applyMiddleware()這個funcrion具體幹了什麼,
     * 只知道他做了一些處理然後重新呼叫了createStore並且enhancer引數為undefined
     * 先記下,後續在看applyMiddleware, 因為我們現在要看的是createStore
     * * */
    // debugger
    return enhancer(createStore)(reducer, preloadedState)
  }
  // debugger

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

  // 簡單過一下定義的變數
  let currentReducer = reducer  // 臨時reducer
  let currentState = preloadedState // 臨時init state
  let currentListeners = []  // 看名字,是個陣列,起名Listeners,想到了什麼? 我想到的是監聽佇列和觀察者模式
  let nextListeners = currentListeners // 淺拷貝下這個佇列
  let isDispatching = false // 我們很容易先假設isDispatching標誌是否正在執行dispatch

  // 先看下各個函式的名字, 打眼一看getState,dispatch,subscribe都是比較熟悉的api
  // subscribe,observable再加上定義的陣列,應該肯定是監聽佇列和觀察者模式
  // 那我們先看看比較熟悉且暴露出來的api好了先看 -> getState

  // 其實這裡是儲存一份訂閱快照
  function ensureCanMutateNextListeners() {
    //  不要忘了let nextListeners = currentListeners // 淺拷貝下這個佇列
    // 判斷nextListeners和當前的currentListeners是不是一個引用
    if (nextListeners === currentListeners) {
      // 如果是一個引用的話深拷貝出來一個currentListeners賦值給nextListener
      nextListeners = currentListeners.slice()
    }
  }

  // store.getState()獲取當前的state
  function getState() {
    // dispatch中不可以getState, 為什麼?
    // 因為dispatch是用來改變state的,為了確保state的正確性(獲取最新的state),所有要判斷啦
    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.'
      )
    }
    // 確定currentState是當前的state 看 -> subscribe
    return currentState
  }

  // store.subscribe方法設定監聽函式,一旦觸發dispatch,就自動執行這個函式
  // listener是一個callback function
  function subscribe(listener) {
    // 型別判斷
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    // 同理不可以dispatch中
    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幹啥的,點選去看一下
    ensureCanMutateNextListeners()
    // push一個function,明顯的觀察者模式,新增一個訂閱函式
    nextListeners.push(listener)
    // 返回取消的function(unsubscribe)
    return function unsubscribe() {
      // 還記得let isSubscribed = true吧, 用來標記是否有listerner的
      if (!isSubscribed) {
        // 沒有直接return
        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,
      isSubscribed = false

      // 儲存訂閱快照
      ensureCanMutateNextListeners()
      // 找到並刪除當前的listener
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  // 傳送一個action
  function dispatch(action) {
    // 看下util的isPlainObject
    // acticon必須是由Object構造的函式, 否則throw Error
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    // 判斷action, 不存在type throw Error
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    // dispatch中不可以有進行的dispatch
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      // 執行時標記為true
      isDispatching = true
      // 執行reducer, 來,回憶一下reducer,引數state, action 返回值newState
      // 這就是dispatch一個action可以改變全域性state的原因
      currentState = currentReducer(currentState, action)
    } finally {
      // 最終執行, isDispatching標記為false, 即完成狀態
      isDispatching = false
    }

    // 監聽佇列
    // 所有的的監聽函式賦值給 listeners
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      // 執行每一個監聽函式
      listener()
    }
    // 返回傳入的action
    return action
    // 到這裡dispatch方法就結束了, 我們來思考總結一下, 為什麼要用listeners
    // 當dispatch傳送一個規範的action時,會更新state
    // 但是state改變了之後我們需要做一些事情, 比如更新ui既資料驅動檢視
    // (當然一般我們使用react,react-redux的時候, 他們會幫我們完成這些事情)
    // 所以要提供一個監聽模式,當然還要有一個監聽函式subscribe, 保證dispatch和subscribe之間的一對多的模式
  }

  // 這是一個高階的api, 用於替換計算 state的reducer,不知道的同學面壁去
  // 哈哈開玩笑的確實很不常用, 官網也沒怎麼介紹
  // redux 熱載入機制的時候用到了
  function replaceReducer(nextReducer) {
    // 既然是替換reducer, 型別要保持一直,不是function的滾遠點
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    // 當前的currentReducer更新為引數nextReducer
    currentReducer = nextReducer
    // 和INIT的dispath相同,傳送一個dispatch初始化state,表明一下是REPLACE
    // 自己?看一下utils方法的ActionTypes, 隨性的隨機數
    dispatch({ type: ActionTypes.REPLACE })
  }

  // 不知道是幹什麼的, 先看看哪裡用到了, 全域性收索一下
  // 我TM!只有這一個地方有這個函式,而且沒被使用( [$$observable]: observable ), 就問你氣不氣?
  // 當然不氣, 作為不思進取的我覺得不用看這部分了, 算了,簡單的過一下, 剛好也不知道$$observable這個私有屬性的作用
  // 好了, 全域性搜尋一下$$observable, 尼瑪,對於我這種碼農來說, 貌似又是沒用的
  // 好吧,我們看一下作者的註釋和程式碼
  function observable() {
    const outerSubscribe = subscribe
    // 
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      // 引數明顯是object
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }
        //獲取觀察著的狀態
        function observeState() {
          // 如果有next方法,吧回撥state
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        //返回取消訂閱的方法
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this // 猜測this應該是store
      }
      // observable方法簡單過一下,不做過多解釋,有了解的同學,歡迎不吝賜教
    }
  }

  // 有沒有想過,在使用redux的時候, 初始化的state哪來的
  // 當然是自己先dispatch了一下
  //reducer 返回其初始狀態 
  //初始化 store 裡的 state tree
  dispatch({ type: ActionTypes.INIT })

  // 這個就是返回的store嘛
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
複製程式碼

看完createStore,已經對redux的大體實現有了一定的瞭解, 接下來我們看combineReducers

先過一下檔案最吸引我的是export default function combineReducers,其他幾個function好像都是用來丟擲一些warning的, 所以先看這個combineReducers function(上面的先不要看?,直接找combineReducers )

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

/**
 *  先過一下檔案最吸引我的是export default function combineReducers
 *  先看這個combineReducers function
 * 
 */
function getUndefinedStateErrorMessage(key, action) {
  const actionType = action && action.type
  const actionDescription =
    (actionType && `action "${String(actionType)}"`) || 'an action'

  return (
    `Given ${actionDescription}, reducer "${key}" returned undefined. ` +
    `To ignore an action, you must explicitly return the previous state. ` +
    `If you want this reducer to hold no value, you can return null instead of undefined.`
  )
}

function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  const reducerKeys = Object.keys(reducers)
  const argumentName =
    action && action.type === ActionTypes.INIT
      ? 'preloadedState argument passed to createStore'
      : 'previous state received by the reducer'

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    )
  }

  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "` +
      {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
      `". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    )
  }

  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })

  if (action && action.type === ActionTypes.REPLACE) return

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    )
  }
}

function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
   // reducer返回值
    const initialState = reducer(undefined, { type: ActionTypes.INIT })
    // undefined throw Error
    if (typeof initialState === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined during initialization. ` +
          `If the state passed to the reducer is undefined, you must ` +
          `explicitly return the initial state. The initial state may ` +
          `not be undefined. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      )
    }

    // 很明顯assertReducerShape是用於reducer的規範
    // 回到combineReducers
    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      throw new Error(
        `Reducer "${key}" returned undefined when probed with a random type. ` +
          `Don't try to handle ${
            ActionTypes.INIT
          } or other actions in "redux/*" ` +
          `namespace. They are considered private. Instead, you must return the ` +
          `current state for any unknown actions, unless it is undefined, ` +
          `in which case you must return the initial state, regardless of the ` +
          `action type. The initial state may not be undefined, but can be null.`
      )
    }
  })
}

// 用於合併reducer 一般是這樣combineReducers({a,b,c})
export default function combineReducers(reducers) {
  // reducers中key的陣列
  const reducerKeys = Object.keys(reducers)
  // 最終的reducer
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    // 接受當前的key
    const key = reducerKeys[i]

    // 如果不是生產環境, 當前的reducer是undefined會給出warning
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }

    // reducer要是一個function
    if (typeof reducers[key] === 'function') {
      // 賦值給finalReducers
      finalReducers[key] = reducers[key]
    }
    // 迴圈結束, 目的為了給finalReducers賦值, 過慮了不符合規範的reudcer
  }
  // 符合規範的reducer的key陣列
  const finalReducerKeys = Object.keys(finalReducers)

  // 意想不到的key, 先往下看看
  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    // production環境為{}
    unexpectedKeyCache = {}
  }

  let shapeAssertionError

  try {
    // 看這個function
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  // 返回function, 即為createstore中的reducer引數既currentreducer
  // 自然有state和action兩個引數, 可以回createstore檔案看看currentReducer(currentState, action)
  return function combination(state = {}, action) {
    // reducer不規範報錯
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    // 比較細緻的❌資訊,順便看了一下getUndefinedStateErrorMessage,都是用於提示warning和error的, 不過多解釋了
    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++) {
      // 獲取finalReducerKeys的key和value(function)
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      // 當前key的state值
      const previousStateForKey = state[key]
      // 執行reducer, 返回當前state
      const nextStateForKey = reducer(previousStateForKey, action)
      // 不存在返回值報錯
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 新的state放在nextState對應的key裡
      nextState[key] = nextStateForKey
      // 判斷新的state是不是同一引用, 以檢驗reducer是不是純函式
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 改變了返回nextState
    return hasChanged ? nextState : state
  }
  /*
  *  新版本的redux這部分改變了實現方法
  *  老版本的redux使用的reduce函式實現的
  *  簡單例子如下
  * function combineReducers(reducers) {
  *    return (state = {}, action) => {
  *        return Object.keys(reducers).reduce((currentState, key) => {
  *            currentState[key] = reducers[key](state[key], action);
  *             return currentState;
  *         }, {})
  *      };
  *    }
  * 
  * */
}

複製程式碼

到這裡,似乎已經基本熟悉了redux的核心, 不過別急,還有重要的applyMiddleware, 哈哈, 先看下bindActionCreator.js緩緩

想必有點同學還不知道這個api,我先看看bindActionCreators作用, 熟悉的同學直接看實現

/**
 *
 * @export
 * @param {*} actionCreators  一個 action creator,或者一個 value 是 action creator 的物件。
 * @param {*} dispatch 一個由 Store 例項提供的 dispatch 函式。
 * @returns 一個與原物件類似的物件,只不過這個物件的 value 都是會直接 dispatch 原 action creator 返回的結果的函式。
 *          如果傳入一個單獨的函式作為 actionCreators,那麼返回的結果也是一個單獨的函式。
 * 
 * 場景: 惟一會使用到 bindActionCreators 的場景是當你需要把 action creator 往下傳到一個元件上,
 *        卻不想讓這個元件覺察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 傳給它。
 * /
複製程式碼

bindActionCreator的實現

function bindActionCreator(actionCreator, dispatch) {
  // 閉包
  return function() {
    // 執行後返回結果為傳入的actionCreator直接呼叫arguments
    return dispatch(actionCreator.apply(this, arguments))
  }
}
// 先看這裡 ???
export default function bindActionCreators(actionCreators, dispatch) {
  // actionCreators為function
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  // 不是object throw Error
  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"?`
    )
  }

  // object 轉為陣列
  const keys = Object.keys(actionCreators)
  // 定義return 的props
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    // actionCreators的key 通常為actionCreators function的name(方法名)
    const key = keys[i]
    // function => actionCreators工廠方法本身
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      // 引數為{actions:function xxx}是返回相同的型別
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  // return 的props
  return boundActionCreators
}
複製程式碼

最後,我們看一下applyMiddeware.js

等等, 先解釋一下compose, 函式程式設計中的方法

 Composes functions from right to left.
複製程式碼

組合函式,將函式串聯起來從右到左執行 這裡的import compose from './compose'可能就是從函式程式設計移過來的

漲這樣:
compose(funcA, funcB, funcC) === compose(funcA(funcB(funcC())))
複製程式碼
 import compose from './compose'

/***
 * 
 * middleware既中介軟體,簡單說在redux中作為擴充套件 dispatch 的唯一標準的方式。
 * 不熟悉的同學自行去api瞭解一下, 大致結構是這樣的
 *  middleware = (store) => (next) =>(action) =>{ [return next(action)]}
 *  為了方便debugger我們先自己寫一個簡單的logger middleware,看->src/index.js
*/
// applyMiddleware用來新增中介軟體,在修改資料的時候redux通過改造dispatch來實現中介軟體.
// 來吧,揭開applyMiddleware的神祕面紗
export default function applyMiddleware(...middlewares) {
  // 返回一個名為createStore的function
  // 不知道你還是否記得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)
    }
    嗯哼?對上了吧, 有applyMiddleware的時候直接先執行這裡, 沒繞過來的同學debugger一下
  * */
  // 直接return createStore function
  // 這裡我們看下執行順序, 我們寫一點虛擬碼,每一個變數是程式碼中debugger的位置
  /**
   *   createStore.js 
   *   d1 = createStore(reducer, initstate, enhancer){ ... debugger if (typeof enhancer !== 'undefined')}
   * 
   *   d2 =  if (typeof enhancer !== 'undefined') {
                      if (typeof enhancer !== 'function') {
                        throw new Error('Expected the enhancer to be a function.')
                      }
                      debugger
                    return enhancer(createStore)(reducer, preloadedState)
                    }
   *   d3 = if (typeof enhancer !== 'undefined') {} debugger
   *   
   *   d4 =  ... debugger const middlewareAPI = {
                      // copy getState
                      getState: store.getState,
                      dispatch: (...args) => dispatch(...args)
                    }
      
        d5 =    ... debugger  const store = createStore(...args)...
   *   
   *   執行順序
   *   建立store的首先是呼叫createStore(...applyMiddleware()) 大致發生了這樣的流程
   *   createStore(...applyMiddleware()) -> applyMiddleware() -> return function -> d1 -> d2 
   *   接下來
   *   return enhancer(createStore)(reducer, preloadedState) -> d5 -> createStore(...args)再次呼叫createStore -> d1
   *   接下來走d3下面的store初始化 -> dispatch(init) -> d4 -> 組合middleware,合併new dispatch -> 返回增強的store
   */
  return createStore => (...args) => {
    // 儲存createStore(reducer, initstate) || createStore(reducer), 賦值給store
    // debugger
    const store = createStore(...args)
    // 定義了一個dispatch, 呼叫會 throw new Error(dispatching雖然構造middleware但不允許其他middleware應用 )
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }
    // debugger
    // 定義middlewareAPI, 中介軟體中的store  eg ->  logger(store)
    const middlewareAPI = {
      // add getState
      getState: store.getState,
      // 新增dispatch幷包裝一個function, 引數為(reducer, [initstate])
      // 向下看一看middlewareAPI作為引數被回撥回去,不難理解, 告訴dispath不能再middleware外掛中構造
      dispatch: (...args) => dispatch(...args)
    }
    // 呼叫每一個這樣形式的middleware = store => next => action =>{}, 
    // 組成一個這樣[f(next)=>acticon=>next(action)...]的array,賦值給chain
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // debugger
    // compose看 -> compose.js檔案
    // compose(...chain)會形成一個呼叫鏈, next指代下一個函式的註冊, 這就是中介軟體的返回值要是next(action)的原因
    // 如果執行到了最後next就是原生的store.dispatch方法
    dispatch = compose(...chain)(store.dispatch) 
    // 返回增強的store
    return {
      ...store,
      dispatch
    }
  }
}

複製程式碼

最後的最後我們去compose.js做一道題

 // 其實這個很有意思,是函式程式設計中的方法
//  我們來做一到題
//  實現這個樣一個function -> compose(funcA, funcB, funcC) 形象為 compose(funcA(funcB(funcC()))))
//  返回值為一個(...args)=>(funcA(funcB(funcC(...args)))))
/**
 *
 * 你可能會這樣寫, 或者是for迴圈 
 * 
 *  function Compose(...funcs){
      if (funcs.length === 0) {
        return args => args;
      }
      if (funcs.length === 1) {
        return funcs[0]
      }
      const arr = funcs;
      let firstFun = arr[0];
      let len = arr.length;
      let i = 1;
      while(i !== len) {
        firstFun = firstFun(arr[i]);
        i++;
      }
      return firstFun;
    }
 * 
 * 
 */
// 好啦, 我們看看優秀的答案吧 ?

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  // 是不是很巧妙
  // 其實compose是redux作者從函數語言程式設計中移過來的, 有興趣的同學去了解一下
  // 插個話, 因為compose的執行順序原因, 所以有的middleware外掛會要求要放在最後面
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製程式碼

好了,到這裡我們就把redux的原始碼看完了

筆者覺得redux原始碼並沒有很難,主要的精華是他的實現思想,我們在來看看redux的三大原則

1.整個應用的state儲存在store中,有且只存在一個store。

2.store裡面的state是隻讀的,唯一改變state的方法就是派發(dispatch)一個動作(action)。

3.純函式(reducer)修改state,每次返回一個新的state,不能直接修改原物件。

下一次,我們來分析一下這樣做的好處

以及我們可不可以自己寫一個簡單的redux

第一次寫部落格, 居然這麼累?, 以後會堅持更新分享, O(∩_∩)O謝謝各位看官老爺, 歡迎交流,分享,指導。

附上github地址

相關文章