国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

史上最全的 Redux 源碼分析

fai1017 / 2204人閱讀

摘要:訂閱器不應(yīng)該關(guān)注所有的變化,在訂閱器被調(diào)用之前,往往由于嵌套的導(dǎo)致發(fā)生多次的改變,我們應(yīng)該保證所有的監(jiān)聽(tīng)都注冊(cè)在之前。

前言

用 React + Redux 已經(jīng)一段時(shí)間了,記得剛開(kāi)始用Redux 的時(shí)候感覺(jué)非常繞,總搞不起里面的關(guān)系,如果大家用一段時(shí)間Redux又看了它的源碼話,對(duì)你的理解會(huì)有很大的幫助。看完后,在回來(lái)看Redux,有一種 柳暗花明又一村 的感覺(jué) ,.

源碼

我分析的是用 es6 語(yǔ)法的源碼,大家看目錄結(jié)構(gòu),一共有 6 個(gè)問(wèn)件。先說(shuō)下各個(gè)文件大概功能。

applyMiddlewar.js 使用自定義的 middleware 來(lái)擴(kuò)展 Redux

bindActionCreators.js 把 action creators 轉(zhuǎn)成擁有同名 keys 的對(duì)象,使用時(shí)可以直接調(diào)用

combineReducers.js 一個(gè)比較大的應(yīng)用,需要對(duì) reducer 函數(shù) 進(jìn)行拆分,拆分后的每一塊獨(dú)立負(fù)責(zé)管理 state 的一部分

compose.js 從右到左來(lái)組合多個(gè)函數(shù),函數(shù)編程中常用到

createStore.js 創(chuàng)建一個(gè) Redux Store 來(lái)放所有的state

utils/warnimng.js 控制臺(tái)輸出一個(gè)警告,我們可以不用看

我會(huì)按每個(gè)模塊的重要性,去做分析,今天就先把 createStore .js 分享給大家.

createStore.js

注釋很詳細(xì),直接看注釋就可以了

// 導(dǎo)入 lodash ,判斷是否是普通(plain)對(duì)象
import isPlainObject from "lodash/isPlainObject"
//導(dǎo)入 symbol 類型的 observable (symbol類型的屬性,是對(duì)象的私有屬性)
import $$observable from "symbol-observable"

/**
 *定義 Redux Action 的初始化 type
 * 
 */
export var ActionTypes = {
  INIT: "@@redux/INIT"
}

/**
 * 創(chuàng)建一個(gè)Redux store來(lái)存放應(yīng)用所有的 state。應(yīng)用中有且只有一個(gè)store
 *
 * @param {Function} reducer 是一個(gè)函數(shù),接收兩個(gè)參數(shù),分別是當(dāng)前的 state 樹(shù)和
 * 要處理的 action,返回新的 state 樹(shù)
 *
 * @param {any} 初始化時(shí)的state ,在應(yīng)用中,你可以把服務(wù)端傳來(lái)經(jīng)過(guò)處理后的 state
 *傳給它。如果你使用 combineReducers 創(chuàng)建 reducer,它必須是一個(gè)普通對(duì)象,與傳入
 *的 keys 保持同樣的結(jié)構(gòu)。否則,你可以自由傳入任何 reducer 可理解的內(nèi)容。
 *
 * @param {Function} enhancer 是一個(gè)組合的高階函數(shù),返回一個(gè)強(qiáng)化過(guò)的 store creator .
 *                  這與 middleware相似,它也允許你通過(guò)復(fù)合函數(shù)改變 store 接口。
 *
 * @returns {Store} 返回一個(gè)對(duì)象,給外部提供 dispatch, getState, subscribe, replaceReducer, 
 */
export default function createStore(reducer, preloadedState, enhancer) {

  //判斷 preloadedState 是一個(gè)函數(shù)并且 enhancer 是未定義 
  if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
    enhancer = preloadedState  // 把 preloadedState 賦值給 enhancer
    preloadedState = undefined // 把 preloadedState 賦值為 undefined
  }

  //判斷 enhancer 不是 undefined
  if (typeof enhancer !== "undefined") {
    //判斷 enhancer 不是一個(gè)函數(shù)
    if (typeof enhancer !== "function") {
      //拋出一個(gè)異常 (enhancer 必須是一個(gè)函數(shù))
      throw new Error("Expected the enhancer to be a function.")
    }
    //調(diào)用 enhancer ,返回一個(gè)新強(qiáng)化過(guò)的 store creator
    return enhancer(createStore)(reducer, preloadedState)
  }
  
  //判斷 reducer 不是一個(gè)函數(shù)
  if (typeof reducer !== "function") {
    //拋出一個(gè)異常 (reducer 必須是一個(gè)函數(shù))
    throw new Error("Expected the reducer to be a function.")
  }

  
  var currentReducer = reducer        //把 reducer 賦值給 currentReducer
  var currentState = preloadedState   //把 preloadedState 賦值給 currentState
  var currentListeners = []           //初始化 listeners 數(shù)組
  var nextListeners = currentListeners//nextListeners 和 currentListeners 指向同一個(gè)引用
  var isDispatching = false           //標(biāo)記正在進(jìn)行dispatch

  /**
   * 保存一份訂閱快照
   * @return {void}
   */
  function ensureCanMutateNextListeners() {
    //判斷 nextListeners 和 currentListeners 是同一個(gè)引用
    if (nextListeners === currentListeners) {
      
      //通過(guò)數(shù)組的 slice 方法,復(fù)制出一個(gè) listeners ,賦值給 nextListeners
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * 獲取 store 里的當(dāng)前 state tree
   *
   * @returns {any} 返回應(yīng)用中當(dāng)前的state tree
   */
  function getState() {

    //當(dāng)前的state tree
    return currentState
  }

  /**
   *
   * 添加一個(gè) listener . 當(dāng) dispatch action 的時(shí)候執(zhí)行,這時(shí) sate 已經(jīng)發(fā)生了一些變化,
   * 你可以在 listener 函數(shù)調(diào)用 getState() 方法獲取當(dāng)前的 state
   *
   * 你可以在 listener 改變的時(shí)候調(diào)用 dispatch ,要注意
   *
   * 1. 訂閱器(subscriptions) 在每次 dispatch() 調(diào)用之前都會(huì)保存一份快照。
   *    當(dāng)你在正在調(diào)用監(jiān)聽(tīng)器(listener)的時(shí)候訂閱(subscribe)或者去掉訂閱(unsubscribe),
   *    對(duì)當(dāng)前的 dispatch() 不會(huì)有任何影響。但是對(duì)于下一次的 dispatch(),無(wú)論嵌套與否,
   *    都會(huì)使用訂閱列表里最近的一次快照。
   *
   * 2. 訂閱器不應(yīng)該關(guān)注所有 state 的變化,在訂閱器被調(diào)用之前,往往由于嵌套的 dispatch()
   *    導(dǎo)致 state 發(fā)生多次的改變,我們應(yīng)該保證所有的監(jiān)聽(tīng)都注冊(cè)在 dispath() 之前。
   *
   * @param {Function} 要監(jiān)聽(tīng)的函數(shù)
   * @returns {Function} 一個(gè)可以移除監(jiān)聽(tīng)的函數(shù)
   */
  function subscribe(listener) {
    //判斷 listener 不是一個(gè)函數(shù)
    if (typeof listener !== "function") {

      //拋出一個(gè)異常 (listener 必須是一個(gè)函數(shù))
      throw new Error("Expected listener to be a function.")
    }

    //標(biāo)記有訂閱的 listener
    var isSubscribed = true

    //保存一份快照
    ensureCanMutateNextListeners()

    //添加一個(gè)訂閱函數(shù)
    nextListeners.push(listener)
    
    //返回一個(gè)取消訂閱的函數(shù)
    return function unsubscribe() {

      //判斷沒(méi)有訂閱一個(gè) listener
      if (!isSubscribed) {

        //調(diào)用 unsubscribe 方法的時(shí)候,直接 return
        return
      }

      //標(biāo)記現(xiàn)在沒(méi)有一個(gè)訂閱的 listener
      isSubscribed = false
      
      //保存一下訂閱快照
      ensureCanMutateNextListeners()
      //找到當(dāng)前的 listener
      var index = nextListeners.indexOf(listener)
      //移除當(dāng)前的 listener
      nextListeners.splice(index, 1)
    }
  }

  /**
   * dispath action。這是觸發(fā) state 變化的惟一途徑。
   * 
   * @param {Object} 一個(gè)普通(plain)的對(duì)象,對(duì)象當(dāng)中必須有 type 屬性
   *
   * @returns {Object} 返回 dispatch 的 action
   *
   * 注意: 如果你要用自定義的中間件, 它可能包裝 `dispatch()`
   *       返回一些其它東西,如( Promise )
   */
  function dispatch(action) {
    //判斷 action 不是普通對(duì)象。也就是說(shuō)該對(duì)象由 Object 構(gòu)造函數(shù)創(chuàng)建
    if (!isPlainObject(action)) {

      //拋出一個(gè)異常(actions 必須是一個(gè)普通對(duì)象. 或者用自定義的中間件來(lái)處理異步 actions)
      throw new Error(
        "Actions must be plain objects. " +
        "Use custom middleware for async actions."
      )
    }

    // 判斷 action 對(duì)象的 type 屬性等于 undefined 
    if (typeof action.type === "undefined") {

      //拋出一個(gè)異常
      throw new Error(
        "Actions may not have an undefined "type" property. " +
        "Have you misspelled a constant?"
      )
    }
  
    //判斷 dispahch 正在運(yùn)行,Reducer在處理的時(shí)候又要執(zhí)行 dispatch
    if (isDispatching) {
      
      //拋出一個(gè)異常(Reducer在處理的時(shí)候不能 dispatch action)
      throw new Error("Reducers may not dispatch actions.")
    }

    try {

      //標(biāo)記 dispatch 正在運(yùn)行
      isDispatching = true

      //執(zhí)行當(dāng)前 Reducer 函數(shù)返回新的 state
      currentState = currentReducer(currentState, action)

    } finally { // (try catch) 最終會(huì)被執(zhí)行的地方

      //標(biāo)記 dispatch 沒(méi)有在運(yùn)行 
      isDispatching = false
    }

    //所有的的監(jiān)聽(tīng)函數(shù)賦值給 listeners
    var listeners = currentListeners = nextListeners

    //遍歷所有的監(jiān)聽(tīng)函數(shù)
    for (var i = 0; i < listeners.length; i++) {

      // 執(zhí)行每一個(gè)監(jiān)聽(tīng)函數(shù)
      listeners[i]()
    }

    //返回傳入的 action 對(duì)象
    return action
  }

  /**
   * 替換計(jì)算 state 的 reducer。
   *
   * 這是一個(gè)高級(jí) API。
   * 只有在你需要實(shí)現(xiàn)代碼分隔,而且需要立即加載一些 reducer 的時(shí)候才可能會(huì)用到它。
   * 在實(shí)現(xiàn) Redux 熱加載機(jī)制的時(shí)候也可能會(huì)用到。
   *
   * @param {Function} store 要用的下一個(gè) reducer.
   * @returns {void}
   */
  function replaceReducer(nextReducer) {

    //判斷 nextReducer 不是一個(gè)函數(shù)
    if (typeof nextReducer !== "function") {

      //拋出一個(gè)異常 (nextReducer必須是一個(gè)函數(shù))
      throw new Error("Expected the nextReducer to be a function.")
    }

    //當(dāng)前傳入的 nextReducer 賦值給 currentReducer
    currentReducer = nextReducer

    //調(diào)用 dispatch 函數(shù),傳入默認(rèn)的 action
    dispatch({ type: ActionTypes.INIT })
  }

  /**
   *  在 creatorStore 內(nèi)部沒(méi)有看到此方法的調(diào)用
   *  (猜想 : 作者可能想用比較強(qiáng)大,活躍的 observable 庫(kù)替換現(xiàn)在的 publish subscribe)
   *
   * @returns {observable} 狀態(tài)改變的時(shí)候返回最小的 observable.
   * 想要了解跟多關(guān)于 observable 庫(kù),建議看下 
   * https://github.com/zenparsing/es-observable (標(biāo)準(zhǔn) es Observable)
   */
  function observable() {
    //訂閱方法賦值給變量 outerSubscribe
    var outerSubscribe = subscribe
    return {
      /**
       * 這是一個(gè)最小的觀察訂閱方法
       * 
       * @param {Object}  觀察著的任何一個(gè)對(duì)象都可以作為一個(gè) observer.
       * 觀察者應(yīng)該有 `next` 方法
       */
      subscribe(observer) {

        //判斷 observer 是一個(gè)對(duì)象
        if (typeof observer !== "object") {
          //拋出異常
          throw new TypeError("Expected the observer to be an object.")
        }

        //獲取觀察著的狀態(tài)
        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        //返回一個(gè)取消訂閱的方法
        var unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },
      //對(duì)象的私有屬性,暫時(shí)不知道有什么用途
      [$$observable]() {
        return this
      }
    }
  }

  //reducer 返回其初始狀態(tài) 
  //初始化 store 里的 state tree
  dispatch({ type: ActionTypes.INIT })

  //返回 store 暴漏出的接口
  return {
    dispatch, //唯一一個(gè)可以改變 state 的哈按時(shí)
    subscribe, //訂閱一個(gè)狀態(tài)改變后,要觸發(fā)的監(jiān)聽(tīng)函數(shù) 
    getState,  // 獲取 store 里的 state
    replaceReducer, //Redux熱加載的時(shí)候可以替換 Reducer
    [$$observable]: observable //對(duì)象的私有屬性,供內(nèi)部使用
  }
}
結(jié)束語(yǔ)

代碼已經(jīng)放在 githunb 上,大家可以查看 https://github.com/jiechud/redux-source-analyze , 如果對(duì)你有幫助,麻煩請(qǐng) Star 一下吧.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/88051.html

相關(guān)文章

  • 史上最全 Android 中高級(jí)工程師面試復(fù)習(xí)大綱

    摘要:找工作之前看了很多面試題,復(fù)習(xí)資料,但是發(fā)現(xiàn)純看面試題是不行的,因?yàn)榭勘车臇|西是記不牢的,需要知識(shí)成體系才可以,所以筆者整理了一份復(fù)習(xí)大綱,基本涵蓋了中高級(jí)工程師面試所必須知識(shí)點(diǎn),希望可以通過(guò)此文幫助一些想換工作的朋友更好的復(fù)習(xí),準(zhǔn)備面試。 概述 都說(shuō)金三銀四青銅五,這幾個(gè)月份是程序員最好的跳槽時(shí)間,筆者四月初也換了工作。找工作之前看了很多面試題,復(fù)習(xí)資料,但是發(fā)現(xiàn)純看面試題是不行的,因?yàn)榭?..

    chengjianhua 評(píng)論0 收藏0
  • 【進(jìn)階3-1期】JavaScript深入之史上最全--5種this綁定全面解析

    摘要:在嚴(yán)格模式下調(diào)用函數(shù)則不影響默認(rèn)綁定。回調(diào)函數(shù)丟失綁定是非常常見(jiàn)的。因?yàn)橹苯又付ǖ慕壎▽?duì)象,稱之為顯示綁定。調(diào)用時(shí)強(qiáng)制把的綁定到上顯示綁定無(wú)法解決丟失綁定問(wèn)題。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第三期,本周的主題是this全面解析,今天是第9天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重...

    xavier 評(píng)論0 收藏0
  • 技術(shù)棧:史上最全Ceph構(gòu)件及組件分析

    摘要:層確保數(shù)據(jù)一致性和可靠性。保證集群的相關(guān)組件在同一時(shí)刻能夠達(dá)成一致,相當(dāng)于集群的領(lǐng)導(dǎo)層,負(fù)責(zé)收集更新和發(fā)布集群信息。元數(shù)據(jù)服務(wù)器,跟蹤文件層次結(jié)構(gòu)并存儲(chǔ)只供使用的元數(shù)據(jù)。啟迪云-高級(jí)開(kāi)發(fā)工程師 ?侯玉彬前言上一次簡(jiǎn)單的介紹Ceph的過(guò)去和未來(lái)的發(fā)展。這一節(jié)將詳細(xì)介紹Ceph的構(gòu)件以及組件。Ceph存儲(chǔ)架構(gòu)Ceph 存儲(chǔ)集群由幾個(gè)不同的daemon組成,每個(gè)daemon負(fù)責(zé)Ceph 的一個(gè)獨(dú)特...

    big_cat 評(píng)論0 收藏0
  • 史上最全Docker資料推送 ▎ Docker小白進(jìn)階大神計(jì)劃

    摘要:入冬了,寒風(fēng)呼嘯,白雪飄飄,此刻窩在家里學(xué)習(xí)應(yīng)當(dāng)是極好的。為了滿足大家的需求,小編火速為大家整理了史上最全的資料。 showImg(https://segmentfault.com/img/remote/1460000007586577?w=900&h=500); 入冬了,寒風(fēng)呼嘯,白雪飄飄,此刻窩在家里學(xué)習(xí)應(yīng)當(dāng)是極好的。為了滿足大家的需求,小編火速為大家整理了史上最全的Docker資...

    StonePanda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<