欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React中Redux核心原理深入分析

 更新時(shí)間:2022年11月11日 15:35:09   作者:夏天的味道123  
這篇文章主要介紹了如何在React中Redux原理,目前redux在react中使用是最多的,所以我們需要將之前編寫的redux代碼,融入到react當(dāng)中去,本文給大家詳細(xì)講解,需要的朋友可以參考下

一、Redux是什么

眾所周知,Redux最早運(yùn)用于React框架中,是一個(gè)全局狀態(tài)管理器。Redux解決了在開發(fā)過程中數(shù)據(jù)無限層層傳遞而引發(fā)的一系列問題,因此我們有必要來了解一下Redux到底是如何實(shí)現(xiàn)的?

二、Redux的核心思想

Redux主要分為幾個(gè)部分:dispatch、reducer、state。

我們著重看下dispatch,該方法是Redux流程的第一步,在用戶界面中通過執(zhí)行dispatch,傳入相對(duì)應(yīng)的action對(duì)象參數(shù),action是一個(gè)描述類型的對(duì)象,緊接著執(zhí)行reducer,最后整體返回一個(gè)store對(duì)象,我們來看下這部分的源碼:

// 主函數(shù)createStore
// 返回一個(gè)store對(duì)象
export default function createStore(reducer, preloadedState, enhancer) {
  // 增強(qiáng)器
  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.')
  }
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  // 獲取最終的state
  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
  }
  // dispatch
  // 參數(shù)action
  function dispatch(action) {
      // 校驗(yàn)傳入的action
    // action必須是個(gè)對(duì)象,否則拋出錯(cuò)誤信息
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // 檢驗(yàn)action對(duì)象的必要屬性
    // type屬性是action對(duì)象必要的屬性
    // 如果傳入的action沒有type屬性,則拋出錯(cuò)誤信息
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    try {
      isDispatching = true
      // 執(zhí)行傳入的reducer函數(shù)
      // 返回state,給currentState賦值
      currentState = currentReducer(currentState, action)
    } finally {
        // 一個(gè)dispatch執(zhí)行完,還原狀態(tài)
      isDispatching = false
    }
    // 執(zhí)行訂閱函數(shù)隊(duì)列
    // dispatch執(zhí)行的同時(shí)會(huì)一并執(zhí)行訂閱隊(duì)列
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // 返回action
    return action
  }
  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  // 默認(rèn)執(zhí)行一次dispatch,做初始化
  dispatch({ type: ActionTypes.INIT })
  // 返回一個(gè)store對(duì)象
  return {
    dispatch,
    subscribe,
    getState,
    ...
  }
}

通過源碼我們可以基本清楚,通過執(zhí)行createStore方法,最終會(huì)返回一個(gè)store對(duì)象,該對(duì)象主要暴露幾個(gè)屬性,我們主要關(guān)注比較常用的:dispatch、getState、getState,看下實(shí)際用例:參考React實(shí)戰(zhàn)視頻講解:進(jìn)入學(xué)習(xí)

import createStore from 'redux'
// 創(chuàng)建一個(gè)reducer
function reducer(state={}, action) {
    switch(action.type) {
        case 'TEST':
        return {
            ...state,
            test: 'test success'
        }
    }
}
// 返回store
const store = createStore(reducer, initState={})
// 執(zhí)行dispatch
store.dispatch({
    type: 'TEST'
})
const state = store.getState() // 返回 {test: 'TEST'}

三、Redux中間件原理

接下來我們來探討Redux的另一個(gè)重要組成部分—中間件。什么是Redux的中間件?Redux中間件其實(shí)是通過重寫createStore來增強(qiáng)和擴(kuò)展原來的dispatch方法,使其能夠在執(zhí)行dispatch的同時(shí)可以同步執(zhí)行其它方法,比如redux-thunk就是一個(gè)處理異步的中間件:

function createThunkMiddleware(extraArgument) {
    // 中間件規(guī)定格式
    // 閉包返回三層嵌套
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

下載了中間件,那么我們來看下如何使用中間件:

import createStore, {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->applyMiddleWare} from 'reduximport reduxThunk from 'redux-thunk'// 創(chuàng)建一個(gè)reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回store// 中間件作為applyMiddleWare的參數(shù)傳入createStoreconst store = createStore(reducer, initState={},applyMiddleWare(reduxThunk))

我們會(huì)發(fā)現(xiàn),中間件的使用方式是用applyMiddleWare把中間件作為參數(shù)傳入createStore中,那么applyMiddleWare是如何實(shí)現(xiàn)的?在這之前我們先看下createStore方法的第三個(gè)參數(shù)是什么,我們回看下createStore源碼:

export default function createStore(reducer, preloadedState, enhancer) {
  ...
  // 增強(qiáng)器
  // 第三個(gè)參數(shù)是enhancer,也就是我們傳入的applyMiddleWare
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 在這里return了enhancer結(jié)果
    // 傳入了createStore,reducer,preloadedState
    // 實(shí)際上是重寫了createStore
    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}

看完了enhancer的實(shí)際作用,我們可以弄清楚applyMiddleWare的實(shí)現(xiàn)原理,請(qǐng)看源碼:

import compose from './compose'
// 傳入middlewares中間件
export default function applyMiddleware(...middlewares) {
  // 閉包嵌套返回2個(gè)方法
  return createStore => (...args) => {
      // 返回store
    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.'
      )
    }
    // 返回一個(gè)對(duì)象
    // 包含getState方法和dispatch方法
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args) // 返回一個(gè)全新的dispatch方法,不污染原來的dispatch
    }
    // 執(zhí)行中間件第一層方法
    // 回顧下中間的格式:({getState, dispatch}) => next => action => next(action)
    // 這里會(huì)比較繞
    const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 返回一個(gè)中間件的函數(shù)集合[next => action => next(action), next => action => next(action)]
    // 使用compose聚合chain函數(shù)集合
    // 返回新的dispatch
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}

這里可能會(huì)讓人很疑惑,不大清楚的童鞋可以先看下中間件的規(guī)范寫法,這里還有一個(gè)重要的函數(shù)compose,我們來看下compose怎么處理chain函數(shù)集合的,請(qǐng)看源碼:

/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */
// 傳入聚合函數(shù)集合
// 集合為:[next => action => next(action), next => action => next(action)]
// 返回一個(gè)新的函數(shù): (arg) => arg  
export default function compose(...funcs) {
  // 判斷如果沒有則返回一個(gè)新函數(shù)
  // 可以聯(lián)想一下dispatch的定義
  // function dispatch(action) {
      ...
      return action
  }
  if (funcs.length === 0) {
    return arg => arg
  }
  // 判斷如果只有一個(gè)中間件,則直接返回第一個(gè)
  if (funcs.length === 1) {
    return funcs[0]
  }
  // 這里用了reduce函數(shù)
  // 把后一個(gè)的中間件的結(jié)果當(dāng)成參數(shù)傳遞給下一個(gè)中間件
  // 函數(shù)列表的每個(gè)函數(shù)執(zhí)行后返回的還是一個(gè)函數(shù):action => next(action)
  // 這個(gè)函數(shù)就是新的dispatch
  // 最后返回函數(shù):(...args) => action => args(action)
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose的源碼及其簡(jiǎn)潔,但是很精髓,幾乎是整個(gè)中間件最出彩的地方。通過reduce把每個(gè)中間件都執(zhí)行一遍,并且是通過管道式的傳輸,把每個(gè)中間件的返回結(jié)果當(dāng)成參數(shù)傳遞給下一個(gè)中間件,實(shí)現(xiàn)了剝洋蔥式的中間件模式。這里比較難理解,新手可以先寫幾個(gè)簡(jiǎn)單的中間件,然后再去慢慢理解為什么要這么處理,理解后就會(huì)知道這段代碼有多簡(jiǎn)潔了。

四、手寫一個(gè)Redux

源碼解析完了,我們來簡(jiǎn)單實(shí)現(xiàn)一個(gè)redux。

createStore

// 判斷值是否是對(duì)象類型
function isPlainObject(obj) {
    if(!obj) {
        reutrn false
    }
    return Object.prototype.toString.call(obj) === '[object, Object]'
}
export default createStore(reducer, enhancer) {
    // 先判斷有沒有傳入中間件
    // 有則之間返回
    if(typeof enhancer !== 'undefined') {
        // 必需是個(gè)函數(shù)
        // 因?yàn)樾枰獋鲄?
        if(typeof enhancer !== 'function') {
            return
        }
        return enhancer(createStore)(reducer)
    }
    let state = {} // 初始化state
    let listeners = [] // 發(fā)布訂閱函數(shù)隊(duì)列
    // 定義getState 函數(shù)
    function getState() {
        // 直接返回state
        return state
    }
    // 定義dispatch 函數(shù)
    function dispatch(action) {
        try{
            // 執(zhí)行reducer, 返回state
            state = reducer(state, action)
        }catch(e) {
            console.log('dispatch error: 'e)
        } 
        // 訂閱
        listeners.forEach(listener => listener())
        // 返回action
        return action
    }
    // 定義subscribe 函數(shù)
    function subscribe(listener) {
        if(!listener) {
            return
        }
        // 必需是回掉函數(shù)
        // 因?yàn)樾枰赿ispatch里執(zhí)行
        if(typeof listener !== 'function') {
            return
        }
        Listeners.push(listener)
    }
    // 返回對(duì)象:包含getState, dispatch, subscribe 三個(gè)方法
    return {
        getState,
        dispatch,
        subscribe
    }
}

compose

    function compose(...funs) {
        if(!funs) {
            return arg => arg
        }
        if(funs.length === 1) {
            return funs[0]
        }
        // 遍歷傳入函數(shù),返回一個(gè)新函數(shù)
        return funs.reduce((a,b) => (...args) => a(b(...args)))
    }

applyMiddleWare

import compose from './compose'
function applyMiddleWare(...middlewares) {
    return createStore => reducer => {
        // 先返回一個(gè)store
        const store = createStore(reducer)
        // 創(chuàng)建middleApi
        const middleApi = {
            getState: store.getState,
            dispatch: (...args) => store.dispatch(...args) // 返回一個(gè)新的dispatch
        }
        // 注入middleApi
        // 并返回函數(shù)集合
        const chain = middlewares.map(middleWare => middleWare(middleApi))
        // 通過compose函數(shù),執(zhí)行所有中間件,并返回一個(gè)新的dispatch
        const dispatch = compose(...chain)(store.dispatch)
        // 返回store對(duì)象
        return {
            getState: store.getState,
            dispatch
        }
    }
}

logger中間件

    function logger({getState, dispatch}) {
        return function(next) {
            return function(action) {
                console.log('prev')
                next(action)
                console.log('done')
            }
        }
    }

測(cè)試

    import createStore from './myCreateStore'
    import applyMiddleWare from './myApplyMiddleWare'
    import logger from './logger'
    // 創(chuàng)建reducer
    function reducer(state={}, action) {
        switch(action.type) {
            case 'TEST':
            return {
                ...state,
                test: 'test success'
            }
        }
    }
    // 引入中間件
    const middleware = applyMiddleWare(logger)
    const store = createStore(reducer, middleware) // 返回{getState, dispatch}

總結(jié)

至此一個(gè)完整的redux我們就已經(jīng)分析完了,個(gè)人認(rèn)為中間件的compose這里是比較不好理解的點(diǎn),但是只要明白中間件主要要解決的是增強(qiáng)dispatch函數(shù),就可以順著這個(gè)思路去理解。接著再試著寫幾個(gè)中間件,進(jìn)一步理解為什么中間件的格式需要返回嵌套的三層函數(shù),明白了這兩個(gè)點(diǎn),redux的原理也就基本能夠明白了,有問題歡迎在評(píng)論中指出。

到此這篇關(guān)于React中Redux核心原理深入分析的文章就介紹到這了,更多相關(guān)React Redux內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React?useEffect異步操作常見問題小結(jié)

    React?useEffect異步操作常見問題小結(jié)

    本文主要介紹了React?useEffect異步操作常見問題小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • React代碼分割的實(shí)現(xiàn)方法介紹

    React代碼分割的實(shí)現(xiàn)方法介紹

    雖然一直有做react相關(guān)的優(yōu)化,按需加載、dll 分離、服務(wù)端渲染,但是從來沒有從路由代碼分割這一塊入手過,所以下面這篇文章主要給大家介紹了關(guān)于React中代碼分割的方式,需要的朋友可以參考下
    2022-12-12
  • React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解

    React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解

    這篇文章主要介紹了React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式,手動(dòng)路由的跳轉(zhuǎn),主要是通過Link或者NavLink進(jìn)行跳轉(zhuǎn)的,實(shí)際上我們也可以通JavaScript代碼進(jìn)行跳轉(zhuǎn),需要的朋友可以參考下
    2022-11-11
  • 使用ES6語(yǔ)法重構(gòu)React代碼詳解

    使用ES6語(yǔ)法重構(gòu)React代碼詳解

    本篇文章主要介紹了使用ES6語(yǔ)法重構(gòu)React代碼詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 關(guān)于react中useCallback的用法

    關(guān)于react中useCallback的用法

    這篇文章主要介紹了關(guān)于react中useCallback的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • react 原生實(shí)現(xiàn)頭像滾動(dòng)播放的示例

    react 原生實(shí)現(xiàn)頭像滾動(dòng)播放的示例

    這篇文章主要介紹了react 原生實(shí)現(xiàn)頭像滾動(dòng)播放的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • 從零開始最小實(shí)現(xiàn)react服務(wù)器渲染詳解

    從零開始最小實(shí)現(xiàn)react服務(wù)器渲染詳解

    這篇文章主要介紹了從零開始最小實(shí)現(xiàn)react服務(wù)器渲染詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • React使用useEffect解決setState副作用詳解

    React使用useEffect解決setState副作用詳解

    這篇文章主要為大家介紹了React使用useEffect解決setState副作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 深入理解react-router 路由的實(shí)現(xiàn)原理

    深入理解react-router 路由的實(shí)現(xiàn)原理

    這篇文章主要介紹了深入理解react-router 路由的實(shí)現(xiàn)原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • 淺談react-router@4.0 使用方法和源碼分析

    淺談react-router@4.0 使用方法和源碼分析

    這篇文章主要介紹了淺談react-router@4.0 使用方法和源碼分析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-06-06

最新評(píng)論