溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行Redux的源碼解析

發(fā)布時(shí)間:2021-11-17 09:47:59 來源:億速云 閱讀:120 作者:柒染 欄目:web開發(fā)

如何進(jìn)行Redux的源碼解析,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

預(yù)熱

redux 函數(shù)內(nèi)部包含了大量柯里化函數(shù)以及代碼組合思想

柯里化函數(shù)(curry)

通俗的來講,可以用一句話概括柯里化函數(shù):返回函數(shù)的函數(shù)

// example const funcA = (a) => {   return const funcB = (b) => {     return a + b   } };

上述的funcA函數(shù)接收一個(gè)參數(shù),并返回同樣接收一個(gè)參數(shù)的funcB函數(shù)。

柯里化函數(shù)有什么好處呢?

  • 避免了給一個(gè)函數(shù)傳入大量的參數(shù)--我們可以通過柯里化來構(gòu)建類似上例的函數(shù)嵌套,將參數(shù)的代入分離開,更有利于調(diào)試

  • 降低耦合度和代碼冗余,便于復(fù)用

舉個(gè)栗子:

// 已知listA, listB兩個(gè)Array,都由int組成,需要篩選出兩個(gè)Array的交集 const listA = [1, 2, 3, 4, 5]; const listB = [2, 3, 4];  const checkIfDataExist = (list) => {   return (target) => {     return list.some(value => value === target)   }; }; // 調(diào)用一次checkIfDataExist函數(shù),并將listA作為參數(shù)傳入,來構(gòu)建一個(gè)新的函數(shù)。 // 而新函數(shù)的作用則是:檢查傳入的參數(shù)是否存在于listA里 const ifDataExist = checkIfDataExist(listA);  // 使用新函數(shù)來對(duì)listB里的每一個(gè)元素進(jìn)行篩選 const intersectionList = listB.filter(value => ifDataExist(value)); console.log(intersectionList); // [2, 3, 4]

代碼組合(compose)

代碼組合就像是數(shù)學(xué)中的結(jié)合律:

const compose = (f, g) => {   return (x) => {     return f(g(x));   }; }; // 還可以再簡(jiǎn)潔點(diǎn) const compose = (f, g) => (x) => f(g(x));

通過這樣函數(shù)之間的組合,可以大大增加可讀性,效果遠(yuǎn)大于嵌套一大堆的函數(shù)調(diào)用,并且我們可以隨意更改函數(shù)的調(diào)用順序

Redux

combineReducers

// 回顧一下combineReducers的使用格式  // 兩個(gè)reducer const todos = (state = INIT.todos, action) => {   // .... }; const filterStatus = (state = INIT.filterStatus, action) => {   // ... };  const appReducer = combineReducers({   todos,   filterStatus });

還記得combineReducers的黑魔法嗎?即:

  1. 傳入的Object參數(shù)中,對(duì)象的key與value所代表的reducer function同名

  2. 各個(gè)reducer function的名稱和需要傳入該reducer的state參數(shù)同名

源碼標(biāo)注解讀(省略部分):

export default function combineReducers(reducers) {   // ***次篩選,參數(shù)reducers為Object   // 篩選掉reducers中不是function的鍵值對(duì)   var reducerKeys = Object.keys(reducers);   var finalReducers = {}   for (var i = 0; i < reducerKeys.length; i++) {     var key = reducerKeys[i];     if (typeof reducers[key] === 'function') {       finalReducers[key] = reducers[key]     }   }    var finalReducerKeys = Object.keys(finalReducers)    // 二次篩選,判斷reducer中傳入的值是否合法(!== undefined)   // 獲取篩選完之后的所有key   var sanityError   try {     // assertReducerSanity函數(shù)用于遍歷finalReducers中的reducer,檢查傳入reducer的state是否合法     assertReducerSanity(finalReducers)   } catch (e) {     sanityError = e   }      // 返回一個(gè)function。該方法接收state和action作為參數(shù)   return function combination(state = {}, action) {     // 如果之前的判斷reducers中有不法值,則拋出錯(cuò)誤     if (sanityError) {       throw sanityError     }     // 如果不是production環(huán)境則拋出warning     if (process.env.NODE_ENV !== 'production') {       var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action)       if (warningMessage) {         warning(warningMessage)       }     }      var hasChanged = false     var nextState = {}     // 遍歷所有的key和reducer,分別將reducer對(duì)應(yīng)的key所代表的state,代入到reducer中進(jìn)行函數(shù)調(diào)用     for (var i = 0; i < finalReducerKeys.length; i++) {       var key = finalReducerKeys[i]       var reducer = finalReducers[key]       // 這也就是為什么說combineReducers黑魔法--要求傳入的Object參數(shù)中,reducer function的名稱和要和state同名的原因       var previousStateForKey = state[key]       var nextStateForKey = reducer(previousStateForKey, action)       // 如果reducer返回undefined則拋出錯(cuò)誤       if (typeof nextStateForKey === 'undefined') {         var errorMessage = getUndefinedStateErrorMessage(key, action)         throw new Error(errorMessage)       }       // 將reducer返回的值填入nextState       nextState[key] = nextStateForKey       // 如果任一state有更新則hasChanged為true       hasChanged = hasChanged || nextStateForKey !== previousStateForKey     }     return hasChanged ? nextState : state   } }  // 檢查傳入reducer的state是否合法 function assertReducerSanity(reducers) {   Object.keys(reducers).forEach(key => {     var reducer = reducers[key]     // 遍歷全部reducer,并給它傳入(undefined, action)     // 當(dāng)***個(gè)參數(shù)傳入undefined時(shí),則為各個(gè)reducer定義的默認(rèn)參數(shù)     var initialState = reducer(undefined, { type: ActionTypes.INIT })          // ActionTypes.INIT幾乎不會(huì)被定義,所以會(huì)通過switch的default返回reducer的默認(rèn)參數(shù)。如果沒有指定默認(rèn)參數(shù),則返回undefined,拋出錯(cuò)誤     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.`       )     }      var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')     if (typeof reducer(undefined, { type }) === '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.`       )     }   }) }

createStore

// 回顧下使用方法 const store = createStore(reducers, state, enhance);

源碼標(biāo)注解讀(省略部分):

// 對(duì)于未知的action.type,reducer必須返回默認(rèn)的參數(shù)state。這個(gè)ActionTypes.INIT就可以用來監(jiān)測(cè)當(dāng)reducer傳入未知type的action時(shí),返回的state是否合法 export var ActionTypes = {   INIT: '@@redux/INIT' }  export default function createStore(reducer, initialState, enhancer) {   // 檢查你的state和enhance參數(shù)有沒有傳反   if (typeof initialState === 'function' && typeof enhancer === 'undefined') {     enhancer = initialState     initialState = undefined   }   // 如果有傳入合法的enhance,則通過enhancer再調(diào)用一次createStore   if (typeof enhancer !== 'undefined') {     if (typeof enhancer !== 'function') {       throw new Error('Expected the enhancer to be a function.')     }     return enhancer(createStore)(reducer, initialState)   }    if (typeof reducer !== 'function') {     throw new Error('Expected the reducer to be a function.')   }    var currentReducer = reducer   var currentState = initialState   var currentListeners = []   var nextListeners = currentListeners   var isDispatching = false // 是否正在分發(fā)事件    function ensureCanMutateNextListeners() {     if (nextListeners === currentListeners) {       nextListeners = currentListeners.slice()     }   }    // 我們?cè)赼ction middleware中經(jīng)常使用的getState()方法,返回當(dāng)前state   function getState() {     return currentState   }    // 注冊(cè)listener,同時(shí)返回一個(gè)取消事件注冊(cè)的方法。當(dāng)調(diào)用store.dispatch的時(shí)候調(diào)用listener   function subscribe(listener) {     if (typeof listener !== 'function') {       throw new Error('Expected listener to be a function.')     }      var isSubscribed = true      ensureCanMutateNextListeners()     nextListeners.push(listener)      return function unsubscribe() {       if (!isSubscribed) {         return       }        isSubscribed = false       // 從nextListeners中去除掉當(dāng)前l(fā)istener       ensureCanMutateNextListeners()       var index = nextListeners.indexOf(listener)       nextListeners.splice(index, 1)     }   }    // dispatch方法接收的action是個(gè)對(duì)象,而不是方法。   // 這個(gè)對(duì)象實(shí)際上就是我們自定義action的返回值,因?yàn)閐ispatch的時(shí)候,已經(jīng)調(diào)用過我們的自定義action了,比如 dispatch(addTodo())   function dispatch(action) {     if (!isPlainObject(action)) {       throw new Error(         'Actions must be plain objects. ' +         'Use custom middleware for async actions.'       )     }      if (typeof action.type === 'undefined') {       throw new Error(         'Actions may not have an undefined "type" property. ' +         'Have you misspelled a constant?'       )     }     // 調(diào)用dispatch的時(shí)候只能一個(gè)個(gè)調(diào)用,通過dispatch判斷調(diào)用的狀態(tài)     if (isDispatching) {       throw new Error('Reducers may not dispatch actions.')     }      try {       isDispatching = true       currentState = currentReducer(currentState, action)     } finally {       isDispatching = false     }     // 遍歷調(diào)用各個(gè)linster     var listeners = currentListeners = nextListeners     for (var i = 0; i < listeners.length; i++) {       listeners[i]()     }      return action   }   // Replaces the reducer currently used by the store to calculate the state.   function replaceReducer(nextReducer) {     if (typeof nextReducer !== 'function') {       throw new Error('Expected the nextReducer to be a function.')     }      currentReducer = nextReducer     dispatch({ type: ActionTypes.INIT })   }   // 當(dāng)create store的時(shí)候,reducer會(huì)接受一個(gè)type為ActionTypes.INIT的action,使reducer返回他們默認(rèn)的state,這樣可以快速的形成默認(rèn)的state的結(jié)構(gòu)   dispatch({ type: ActionTypes.INIT })    return {     dispatch,     subscribe,     getState,     replaceReducer   } }

thunkMiddleware

源碼及其簡(jiǎn)單簡(jiǎn)直給跪...

// 返回以 dispatch 和 getState 作為參數(shù)的action export default function thunkMiddleware({ dispatch, getState }) {   return next => action => {     if (typeof action === 'function') {       return action(dispatch, getState);     }      return next(action);   }; }

applyMiddleware

先復(fù)習(xí)下用法:

// usage import {createStore, applyMiddleware} from 'redux'; import thunkMiddleware from 'redux-thunk';  const store = createStore(       reducers,       state,       applyMiddleware(thunkMiddleware) );

applyMiddleware首先接收thunkMiddleware作為參數(shù),兩者組合成為一個(gè)新的函數(shù)(enhance),之后在createStore內(nèi)部,因?yàn)閑nhance的存在,將會(huì)變成返回enhancer(createStore)(reducer,  initialState)

源碼標(biāo)注解讀(省略部分):

// 定義一個(gè)代碼組合的方法 // 傳入一些function作為參數(shù),返回其鏈?zhǔn)秸{(diào)用的形態(tài)。例如, // compose(f, g, h) 最終返回 (...args) => f(g(h(...args))) export default function compose(...funcs) {   if (funcs.length === 0) {     return arg => arg   } else {     const last = funcs[funcs.length - 1]     const rest = funcs.slice(0, -1)     return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))   } }  export default function applyMiddleware(...middlewares) {   // 最終返回一個(gè)以createStore為參數(shù)的匿名函數(shù)   // 這個(gè)函數(shù)返回另一個(gè)以reducer, initialState, enhancer為參數(shù)的匿名函數(shù)   return (createStore) => (reducer, initialState, enhancer) => {     var store = createStore(reducer, initialState, enhancer)     var dispatch     var chain = []      var middlewareAPI = {       getState: store.getState,       dispatch: (action) => dispatch(action)     }     // 每個(gè) middleware 都以 middlewareAPI 作為參數(shù)進(jìn)行注入,返回一個(gè)新的鏈。此時(shí)的返回值相當(dāng)于調(diào)用 thunkMiddleware 返回的函數(shù): (next) => (action) => {} ,接收一個(gè)next作為其參數(shù)     chain = middlewares.map(middleware => middleware(middlewareAPI))     // 并將鏈代入進(jìn) compose 組成一個(gè)函數(shù)的調(diào)用鏈     // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函數(shù)對(duì)象。     // 在目前只有 thunkMiddleware 作為 middlewares 參數(shù)的情況下,將返回 (next) => (action) => {}     // 之后以 store.dispatch 作為參數(shù)進(jìn)行注入     dispatch = compose(...chain)(store.dispatch)      return {       ...store,       dispatch     }   } }

一臉懵逼?沒關(guān)系,來結(jié)合實(shí)際使用總結(jié)一下:

當(dāng)我們搭配redux-thunk這個(gè)庫的時(shí)候,在redux配合components時(shí),通常這么寫

import thunkMiddleware from 'redux-thunk'; import { createStore, applyMiddleware, combineReducer } from 'redux'; import * as reducers from './reducers.js';  const appReducer = combineReducer(reducers); const store = createStore(appReducer, initialState, applyMiddleware(thunkMiddleware));

還記得當(dāng)createStore收到的參數(shù)中有enhance時(shí)會(huì)怎么做嗎?

// createStore.js if (typeof enhancer !== 'undefined') {   if (typeof enhancer !== 'function') {     throw new Error('Expected the enhancer to be a function.')   }   return enhancer(createStore)(reducer, initialState) }

也就是說,會(huì)變成下面的情況

applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState)
  • applyMiddleware(thunkMiddleware)

applyMiddleware接收thunkMiddleware作為參數(shù),返回形如(createStore) => (reducer,  initialState, enhancer) => {}的函數(shù)。

  • applyMiddleware(thunkMiddleware)(createStore)

以 createStore 作為參數(shù),調(diào)用上一步返回的函數(shù)(reducer, initialState, enhancer) => {}

  • applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState)

以(reducer, initialState)為參數(shù)進(jìn)行調(diào)用。

在這個(gè)函數(shù)內(nèi)部,thunkMiddleware被調(diào)用,其作用是監(jiān)測(cè)type是function的action

因此,如果dispatch的action返回的是一個(gè)function,則證明是中間件,則將(dispatch,  getState)作為參數(shù)代入其中,進(jìn)行action  內(nèi)部下一步的操作。否則的話,認(rèn)為只是一個(gè)普通的action,將通過next(也就是dispatch)進(jìn)一步分發(fā)。

也就是說,applyMiddleware(thunkMiddleware)作為enhance,最終起了這樣的作用:

對(duì)dispatch調(diào)用的action(例如,dispatch(addNewTodo(todo)))進(jìn)行檢查,如果action在***次調(diào)用之后返回的是function,則將(dispatch,  getState)作為參數(shù)注入到action返回的方法中,否則就正常對(duì)action進(jìn)行分發(fā),這樣一來我們的中間件就完成嘍~

因此,當(dāng)action內(nèi)部需要獲取state,或者需要進(jìn)行異步操作,在操作完成之后進(jìn)行事件調(diào)用分發(fā)的話,我們就可以讓action  返回一個(gè)以(dispatch, getState)為參數(shù)的function而不是通常的Object,enhance就會(huì)對(duì)其進(jìn)行檢測(cè)以便正確的處理。

bindActionCreator

這個(gè)方法感覺比較少見,我個(gè)人也很少用到

在傳統(tǒng)寫法下,當(dāng)我們要把 state 和 action 注入到子組件中時(shí),一般會(huì)這么做:

import { connect } from 'react-redux'; import {addTodo, deleteTodo} from './action.js';  class TodoComponect extends Component {   render() {     return (       <ChildComponent          deleteTodo={this.props.deleteTodo}         addTodo={this.props.addTodo}       />     )   } }  function mapStateToProps(state) {   return {     state   } } function mapDispatchToProps(dispatch) {   return {     deleteTodo: (id) => {       dispatch(deleteTodo(id));     },     addTodo: (todo) => {       dispatch(addTodo(todo));     }   } } export default connect(mapStateToProps, mapDispatchToProps)(TodoComponect);

使用bindActionCreators可以把 action 轉(zhuǎn)為同名 key 的對(duì)象,但使用 dispatch 把每個(gè) action  包圍起來調(diào)用

惟一使用 bindActionCreators 的場(chǎng)景是當(dāng)你需要把 action creator 往下傳到一個(gè)組件上,卻不想讓這個(gè)組件覺察到 Redux  的存在,而且不希望把 Redux store 或 dispatch 傳給它。

// 在本組件內(nèi)的應(yīng)用   addTodo(todo) {     let action = TodoActions.addTodo(todo);     this.props.dispatch(action);   }      deleteTodo(id) {     let action = TodoActions.deleteTodo(id);     this.props.dispatch(action);   }      render() {     let dispatch = this.props.dispatch;     // 傳遞給子組件     let boundActionCreators = bindActionCreators(TodoActions, dispatch);     return (       <ChildComponent          {...boundActionCreators}       />     )   } }  function mapStateToProps(state) {   return {     state   } } export default connect(mapStateToProps)(TodoComponect)

bindActionCreator源碼解析

function bindActionCreator(actionCreator, dispatch) {   return (...args) => dispatch(actionCreator(...args)) }  // bindActionCreators期待一個(gè)Object作為actionCreators傳入,里面是 key: action export default function bindActionCreators(actionCreators, dispatch) {   // 如果只是傳入一個(gè)action,則通過bindActionCreator返回被綁定到dispatch的函數(shù)   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"?`     )   }    // 遍歷并通過bindActionCreator分發(fā)綁定至dispatch   var keys = Object.keys(actionCreators)   var boundActionCreators = {}   for (var i = 0; i < keys.length; i++) {     var key = keys[i]     var actionCreator = actionCreators[key]     if (typeof actionCreator === 'function') {       boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)     }   }   return boundActionCreators }

react-redux

Provider

export default class Provider extends Component {   getChildContext() {     // 將其聲明為 context 的屬性之一     return { store: this.store }   }    constructor(props, context) {     super(props, context)     // 接收 redux 的 store 作為 props     this.store = props.store   }    render() {     return Children.only(this.props.children)   } }  if (process.env.NODE_ENV !== 'production') {   Provider.prototype.componentWillReceiveProps = function (nextProps) {     const { store } = this     const { store: nextStore } = nextProps      if (store !== nextStore) {       warnAboutReceivingStore()     }   } }  Provider.propTypes = {   store: storeShape.isRequired,   children: PropTypes.element.isRequired } Provider.childContextTypes = {   store: storeShape.isRequired }

connect

傳入mapStateToProps,mapDispatchToProps,mergeProps,options。

首先獲取傳入的參數(shù),如果沒有則以默認(rèn)值代替

const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars const defaultMapDispatchToProps = dispatch => ({ dispatch }) const { pure = true, withRef = false } = options

之后,通過

const finalMergeProps = mergeProps || defaultMergeProps

選擇合并stateProps,dispatchProps,parentProps的方式,默認(rèn)的合并方式 defaultMergeProps 為:

const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({   ...parentProps,   ...stateProps,   ...dispatchProps })

返回一個(gè)以 Component 作為參數(shù)的函數(shù)。在這個(gè)函數(shù)內(nèi)部,生成了一個(gè)叫做Connect的 Component

// ...   return function wrapWithConnect(WrappedComponent) {     const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`     // 檢查參數(shù)合法性     function checkStateShape(props, methodName) {}     // 合并props     function computeMergedProps(stateProps, dispatchProps, parentProps) {       const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)       if (process.env.NODE_ENV !== 'production') {         checkStateShape(mergedProps, 'mergeProps')       }       return mergedProps     }          // start of Connect     class Connect extends Component {       constructor(props, context) {         super(props, context);         this.store = props.store || context.store                  const storeState = this.store.getState()         this.state = { storeState }         this.clearCache()       }              computeStateProps(store, props) {         // 調(diào)用configureFinalMapState,使用傳入的mapStateToProps方法(或默認(rèn)方法),將state map進(jìn)props       }       configureFinalMapState(store, props) {}              computeDispatchProps(store, props) {         // 調(diào)用configureFinalMapDispatch,使用傳入的mapDispatchToProps方法(或默認(rèn)方法),將action使用dispatch封裝map進(jìn)props       }       configureFinalMapDispatch(store, props) {}              // 判斷是否更新props       updateStatePropsIfNeeded() {}       updateDispatchPropsIfNeeded() {}       updateMergedPropsIfNeeded() {}              componentDidMount() {         // 內(nèi)部調(diào)用this.store.subscribe(this.handleChange.bind(this))         this.trySubscribe()       }       handleChange() {         const storeState = this.store.getState()         const prevStoreState = this.state.storeState         // 對(duì)數(shù)據(jù)進(jìn)行監(jiān)聽,發(fā)送改變時(shí)調(diào)用         this.setState({ storeState })       }              // 取消監(jiān)聽,清除緩存       componentWillUnmount() {         this.tryUnsubscribe()         this.clearCache()       }              render() {         this.renderedElement = createElement(WrappedComponent,             this.mergedProps         )         return this.renderedElement       }     }     // end of Connect          Connect.displayName = connectDisplayName     Connect.WrappedComponent = WrappedComponent     Connect.contextTypes = {       store: storeShape     }     Connect.propTypes = {       store: storeShape     }          return hoistStatics(Connect, WrappedComponent)   } // ...

我們看見,在connect的***,返回了使用hoistStatics包裝的Connect和WrappedComponent

hoistStatics是什么鬼?為什么使用它?

Copies non-react specific statics from a child component to a parent  component. Similar to Object.assign, but with React static keywords blacklisted  from being overridden.

也就是說,它類似于Object.assign,作用是將子組件中的 static 方法復(fù)制進(jìn)父組件,但不會(huì)覆蓋組件中的關(guān)鍵字方法(如  componentDidMount)

import hoistNonReactStatic from 'hoist-non-react-statics';  hoistNonReactStatic(targetComponent, sourceComponent);

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI