成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

解密Redux: 從源碼開(kāi)始

remcarpediem / 2634人閱讀

摘要:接下來(lái)筆者就從源碼中探尋是如何實(shí)現(xiàn)的。其實(shí)很簡(jiǎn)單,可以簡(jiǎn)單理解為一個(gè)約束了特定規(guī)則并且包括了一些特殊概念的的發(fā)布訂閱器。新舊中存在的任何都將收到先前的狀態(tài)。這有效地使用來(lái)自舊狀態(tài)樹(shù)的任何相關(guān)數(shù)據(jù)填充新?tīng)顟B(tài)樹(shù)。

Redux是當(dāng)今比較流行的狀態(tài)管理庫(kù),它不依賴(lài)于任何的框架,并且配合著react-redux的使用,Redux在很多公司的React項(xiàng)目中起到了舉足輕重的作用。接下來(lái)筆者就從源碼中探尋Redux是如何實(shí)現(xiàn)的。

注意:本文不去過(guò)多的講解Redux的使用方法,更多的使用方法和最佳實(shí)踐請(qǐng)移步Redux官網(wǎng)。
源碼之前 基礎(chǔ)概念

隨著我們項(xiàng)目的復(fù)雜,項(xiàng)目中的狀態(tài)就變得難以維護(hù)起來(lái),這些狀態(tài)在什么時(shí)候,處于什么原因,怎樣變化的我們就很難去控制。因此我們考慮在項(xiàng)目中引入諸如Redux、Mobx這樣的狀態(tài)管理工具。

Redux其實(shí)很簡(jiǎn)單,可以簡(jiǎn)單理解為一個(gè)約束了特定規(guī)則并且包括了一些特殊概念的的發(fā)布訂閱器。

在Redux中,我們用一個(gè)store來(lái)管理一個(gè)一個(gè)的state。當(dāng)我們想要去修改一個(gè)state的時(shí)候,我們需要去發(fā)起一個(gè)action,這個(gè)action告訴Redux發(fā)生了哪個(gè)動(dòng)作,但是action不能夠去直接修改store里頭的state,他需要借助reducer來(lái)描述這個(gè)行為,reducer接受state和action,來(lái)返回新的state。

三大原則

在Redux中有三大原則:

單一數(shù)據(jù)源:所有的state都存儲(chǔ)在一個(gè)對(duì)象中,并且這個(gè)對(duì)象只存在于唯一的store中;

state只讀性:唯一改變state的方法就是去觸發(fā)一個(gè)action,action用來(lái)描述發(fā)生了哪個(gè)行為;

使用純函數(shù)來(lái)執(zhí)行修改:reducer描述了action如何去修改state,reducer必須是一個(gè)純函數(shù),同樣的輸入必須有同樣的輸出;

剖析源碼 項(xiàng)目結(jié)構(gòu)

拋去一些項(xiàng)目的配置文件和其他,Redux的源碼其實(shí)很少很簡(jiǎn)單:

index.js:入口文件,導(dǎo)出另外幾個(gè)核心函數(shù);

createStore.js:store相關(guān)的核心代碼邏輯,本質(zhì)是一個(gè)發(fā)布訂閱器;

combineReducers.js:用來(lái)合并多個(gè)reducer到一個(gè)root reducer的相關(guān)邏輯;

bindActionCreators.js:用來(lái)自動(dòng)dispatch的一個(gè)方法;

applyMiddleware.js:用來(lái)處理使用的中間件;

compose.js:導(dǎo)出一個(gè)通過(guò)從右到左組合參數(shù)函數(shù)獲得的函數(shù);

utils:兩個(gè)個(gè)工具函數(shù)和一個(gè)系統(tǒng)注冊(cè)的actionType;

從createStore來(lái)講一個(gè)store的創(chuàng)建

首先我們先通過(guò)createStore函數(shù)的入?yún)⒑头祷刂祦?lái)簡(jiǎn)要理解它的功能:

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

  // ...

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

createStore接受三個(gè)參數(shù):

reducer:用來(lái)描述action如何改變state的方法,它給定當(dāng)前state和要處理的action,返回下一個(gè)state;

preloadedState:顧名思義就是初始化的state;

enhancer:可以直譯為增強(qiáng)器,用它來(lái)增強(qiáng)store的第三方功能,Redux附帶的唯一store增強(qiáng)器是applyMiddleware;

createStore返回一個(gè)對(duì)象,對(duì)象中包含使用store的基本函數(shù):

dispatch:用于action的分發(fā);

subscribe:訂閱器,他將會(huì)在每次action被dispatch的時(shí)候調(diào)用;

getState:獲取store中的state值;

replaceReducer:替換reducer的相關(guān)邏輯;

接下來(lái)我們來(lái)看看createStore的核心邏輯,這里我省略了一些簡(jiǎn)單的警告和判斷邏輯:

export default function createStore(reducer, preloadedState, enhancer) {
  // 判斷是不是傳入了過(guò)多的enhancer
  // ...

  // 如果不傳入preloadedState只傳入enhancer可以寫(xiě)成,const store = createStore(reducers, enhancer)
  // ...

  // 通過(guò)在增強(qiáng)器傳入createStore來(lái)增強(qiáng)store的基本功能,其他傳入的參數(shù)作為返回的高階函數(shù)參數(shù)傳入;
  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.")
  }

  // 閉包內(nèi)的變量;
  // state作為內(nèi)部變量不對(duì)外暴露,保持“只讀”性,僅通過(guò)reducer去修改
  let currentReducer = reducer
  let currentState = preloadedState
  // 確保我們所操作的listener列表不是原始的listener列表,僅是他的一個(gè)副本;
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  // 確保我們所操作的listener列表不是原始的listener列表,僅是他的一個(gè)副本;
  // 只有在dispatch的時(shí)候,才會(huì)去將currentListeners和nextListeners更新成一個(gè);
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  // 通過(guò)閉包返回了state,state僅可以通過(guò)此方法訪(fǎng)問(wèn);
  function getState() {
    // 判斷當(dāng)前是否在dispatch過(guò)程中
    // ...

    return currentState
  }

  // Redux內(nèi)部的發(fā)布訂閱器
  function subscribe(listener) {
    // 判斷l(xiāng)istener的合法性
    // ...

    // 判斷當(dāng)前是否在dispatch過(guò)程中
    // ...

    let isSubscribed = true

    // 復(fù)制一份當(dāng)前的listener副本
    // 操作的都是副本而不是源數(shù)據(jù)
    ensureCanMutateNextListeners()
    nextListeners.push(listener)

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

      // 判斷當(dāng)前是否在dispatch過(guò)程中
      // ...

      isSubscribed = false

      ensureCanMutateNextListeners()

      // 根據(jù)當(dāng)前l(fā)istener的索引從listener數(shù)組中刪除來(lái)實(shí)現(xiàn)取掉訂閱;
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    // 判斷action是不是一個(gè)普通對(duì)象;
    // ...

    // 判斷action的type是否合法
    // ...

    // 判斷當(dāng)前是否在dispatch過(guò)程中
    // ...

    try {
      isDispatching = true
      // 根據(jù)要觸發(fā)的action, 通過(guò)reducer來(lái)更新當(dāng)前的state;
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    // 通知listener執(zhí)行對(duì)應(yīng)的操作;
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  // 替換reducer,修改state變化的邏輯
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== "function") {
      throw new Error("Expected the nextReducer to be a function.")
    }

    currentReducer = nextReducer

    // 此操作對(duì)ActionTypes.INIT具有類(lèi)似的效果。
    // 新舊rootReducer中存在的任何reducer都將收到先前的狀態(tài)。
    // 這有效地使用來(lái)自舊狀態(tài)樹(shù)的任何相關(guān)數(shù)據(jù)填充新?tīng)顟B(tài)樹(shù)。
    dispatch({ type: ActionTypes.REPLACE })
  }

  function observable() {
    const outerSubscribe = subscribe
    return {
      // 任何對(duì)象都可以被用作observer,observer對(duì)象應(yīng)該有一個(gè)next方法
      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)
        // 返回一個(gè)帶有unsubscribe方法的對(duì)象可以被用來(lái)在store中取消訂閱
        return { unsubscribe }
      },

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

  // 創(chuàng)建store時(shí),將調(diào)度“INIT”操作,以便每個(gè)reducer返回其初始狀態(tài),以便state的初始化。
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}
從combineReducers談store的唯一性

僅靠上面的createStore其實(shí)已經(jīng)可以完成一個(gè)簡(jiǎn)單的狀態(tài)管理了,但是隨著業(yè)務(wù)體量的增大,state、action、reducer也會(huì)隨之增大,我們不可能把所有的東西都塞到一個(gè)reducer里,最好是劃分成不同的reducer來(lái)處理不同模塊的業(yè)務(wù)。

但是也不能創(chuàng)建多個(gè)store維護(hù)各自的reducer,這就違背了Redux的單一store原則。為此,Redux提供了combineReducers讓我們將按照業(yè)務(wù)模塊劃分的reducer合成一個(gè)rootReducer。

接下來(lái)我們看看combineReducers的源碼,這里也是去掉了一些錯(cuò)誤警告的代碼和一些錯(cuò)誤處理方法:

export default function combineReducers(reducers) {
  // 取出所有的reducer遍歷合并到一個(gè)對(duì)象中
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    // 判斷未匹配的refucer
    // ...

    if (typeof reducers[key] === "function") {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

   // 錯(cuò)誤處理的一些邏輯
   // ...

  return function combination(state = {}, action) {

    // 錯(cuò)誤處理的一些邏輯
    // ...

    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      // 對(duì)應(yīng)的reducer
      const reducer = finalReducers[key]
      // 根據(jù)指定的reducer找到對(duì)應(yīng)的state
      const previousStateForKey = state[key]
      // 執(zhí)行reducer, 返回當(dāng)前state
      const nextStateForKey = reducer(previousStateForKey, action)
      // nextStateForKey undefined的一些判斷
      // ...

      // 整合每一個(gè)reducer對(duì)應(yīng)的state
      nextState[key] = nextStateForKey
      // 判斷新的state是不是同一引用, 以檢驗(yàn)reducer是不是純函數(shù)
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

其實(shí)到這里可以簡(jiǎn)單的看出combineReducers就是把多個(gè)reducer拉伸展開(kāi)到到一個(gè)對(duì)象里,同樣也把每一個(gè)reducer里的state拉伸到一個(gè)對(duì)象里。

從bindActionCreators談如何自動(dòng)dispatch

現(xiàn)有的store每一次state的更新都需要手動(dòng)的dispatch每一個(gè)action,而我們其實(shí)更需要的是自動(dòng)的dispatch所有的action。這里就用到了bindActionCreators方法。

現(xiàn)在我們來(lái)看看bindActionCreators的源碼

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

export default function bindActionCreators(actionCreators, dispatch) {
  // 返回綁定了this的actionCreator
  if (typeof actionCreators === "function") {
    return bindActionCreator(actionCreators, dispatch)
  }

  // actionCreators類(lèi)型判斷的錯(cuò)誤處理
  // ...

  // 為每一個(gè)actionCreator綁定this
  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === "function") {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

其實(shí)我們?cè)趓eact項(xiàng)目中對(duì)這個(gè)方法是幾乎無(wú)感知的,因?yàn)槭窃趓eact-redux的connect中調(diào)用了這個(gè)方法來(lái)實(shí)現(xiàn)自動(dòng)dispatch action的,不然需要手動(dòng)去dispatch一個(gè)個(gè)action。

從compose談函數(shù)組合

compose是Redux導(dǎo)出的一個(gè)方法,這方法就是利用了函數(shù)式的思想對(duì)函數(shù)進(jìn)行組合:

// 通過(guò)從右到左組合參數(shù)函數(shù)獲得的函數(shù)。例如,compose(f, g, h)與do(...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)))
}
從applyMiddleware談如何自定義dispatch

我們的action會(huì)出現(xiàn)同步的場(chǎng)景,當(dāng)然也會(huì)出現(xiàn)異步的場(chǎng)景,在這兩種場(chǎng)景下dispacth的執(zhí)行時(shí)機(jī)是不同的,在Redux中,可以使用middleware來(lái)對(duì)dispatch進(jìn)行改造,下面我們來(lái)看看applyMiddleware的實(shí)現(xiàn):

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."
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 通過(guò)從右到左組合參數(shù)函數(shù)獲得的函數(shù)。例如,compose(f, g, h)與do(...args)=> f(g(h(... args)))相同。
    // 對(duì)dispatch改造
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
結(jié)語(yǔ)

到此,Redux源碼的部分就分析完了,但是在具體和React結(jié)合的時(shí)候還需要用到react-redux,下一篇文章,我將深入到react-redux的源碼學(xué)習(xí),來(lái)探索在react中,我們?nèi)绾稳ナ褂肦edux。

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

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

相關(guān)文章

  • 前端最實(shí)用書(shū)簽(持續(xù)更新)

    摘要:前言一直混跡社區(qū)突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來(lái)有點(diǎn)混亂所以將前端主流技術(shù)做了一個(gè)書(shū)簽整理不求最多最全但求最實(shí)用。 前言 一直混跡社區(qū),突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來(lái)有點(diǎn)混亂; 所以將前端主流技術(shù)做了一個(gè)書(shū)簽整理,不求最多最全,但求最實(shí)用。 書(shū)簽源碼 書(shū)簽導(dǎo)入瀏覽器效果截圖showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 評(píng)論0 收藏0
  • React 之容器組件和展示組件相分離解密

    摘要:的綁定庫(kù)包含了容器組件和展示組件相分離的開(kāi)發(fā)思想。明智的做法是只在最頂層組件如路由操作里使用。其余內(nèi)部組件僅僅是展示性的,所有數(shù)據(jù)都通過(guò)傳入。 Redux 的 React 綁定庫(kù)包含了 容器組件和展示組件相分離 的開(kāi)發(fā)思想。明智的做法是只在最頂層組件(如路由操作)里使用 Redux。其余內(nèi)部組件僅僅是展示性的,所有數(shù)據(jù)都通過(guò) props 傳入。 那么為什么需要容器組件和展示組件相分離呢...

    QLQ 評(píng)論0 收藏0
  • 結(jié)合 Google quicklink,react 項(xiàng)目實(shí)現(xiàn)頁(yè)面秒開(kāi)

    摘要:最后,狀態(tài)管理與同構(gòu)實(shí)戰(zhàn)這本書(shū)由我和前端知名技術(shù)大佬顏海鏡合力打磨,凝結(jié)了我們?cè)趯W(xué)習(xí)實(shí)踐框架過(guò)程中的積累和心得。 對(duì)于前端資訊比較敏感的同學(xué),可能這兩天已經(jīng)聽(tīng)說(shuō)了 GoogleChromeLabs/quicklink這個(gè)項(xiàng)目:它由 Google 公司著名開(kāi)發(fā)者 Addy Osmani 發(fā)起,實(shí)現(xiàn)了:在空閑時(shí)間預(yù)獲取頁(yè)面可視區(qū)域內(nèi)的鏈接,加快后續(xù)加載速度。如果你沒(méi)有聽(tīng)說(shuō)過(guò) Addy Os...

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

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

0條評(píng)論

閱讀需要支付1元查看
<