一起學(xué)react(4) 史上最詳細(xì)react-redux 源碼分析

今天來(lái)分析一下react-redux源碼
react-redux所有代碼注釋地址:https://github.com/fangkyi03/react-redux.git
如果大家有問(wèn)題的話(huà) 可以加我QQ:469373256
寫(xiě)完這個(gè)文章我也是心力交瘁了 react-redux 一些數(shù)據(jù)都是放在閉包里面的 然后循環(huán)的調(diào)用有點(diǎn)蒙圈的感覺(jué)

  • 接口預(yù)覽
    react-redux只暴露了這幾個(gè)接口 接下來(lái) 將會(huì)按照代碼的執(zhí)行順序 進(jìn)行一一的分析
import Provider, { createProvider } from './components/Provider'
import connectAdvanced from './components/connectAdvanced'
import connect from './connect/connect'

export { Provider, createProvider, connectAdvanced, connect }
  • Provider
    先來(lái)看一下函數(shù)定義部分
export function createProvider(storeKey = 'store', subKey) 
export default createProvider()
import Provider, { createProvider } from './components/Provider'

我們默認(rèn)如果直接使用react-redux中的Provider的話(huà) 默認(rèn)傳遞過(guò)去的數(shù)據(jù)是不可控制的 如果你想做一些自定義設(shè)置的話(huà) 建議選擇引入createProvider

  • Provider 主體部分
export function createProvider(storeKey = 'store', subKey) {
    如果subKey有值就使用傳入的值 否則使用store
     所以最后的結(jié)果就是變成storeSubscription
    const subscriptionKey = subKey || `${storeKey}Subscription`

    class Provider extends Component {
        react-redux使用context來(lái)實(shí)現(xiàn)父級(jí)對(duì)所有的子級(jí)傳遞數(shù)據(jù) 但是這個(gè)context已經(jīng)在16.3版本有了新的方法 這里就不多講了
          
        首先 如果你要讓子獲取到父的數(shù)據(jù) 那么肯定得有一個(gè)入口 這個(gè)getChildContext就是子固定的入口 它會(huì)去判斷自身的ContextTypes是否有數(shù)據(jù) 如果有的話(huà) 就會(huì)直接調(diào)用父級(jí)的這個(gè)getChildContext來(lái)返回對(duì)應(yīng)的數(shù)據(jù)
        getChildContext() {
          return { [storeKey]: this[storeKey], [subscriptionKey]: null }
        }

        constructor(props, context) {
          super(props, context)
          this[storeKey] = props.store;
        }

        render() {
          只允許渲染一個(gè)
          return Children.only(this.props.children)
        }
    }
    如果不是線(xiàn)上模式的話(huà)并且如果這個(gè)組件的父發(fā)生改變的話(huà) 將會(huì)出現(xiàn)錯(cuò)誤提醒
    if (process.env.NODE_ENV !== 'production') {
      Provider.prototype.componentWillReceiveProps = function (nextProps) {
        if (this[storeKey] !== nextProps.store) {
          warnAboutReceivingStore()
        }
      }
    }
    
   對(duì)于props組件類(lèi)型的定義
    Provider.propTypes = {
        store: storeShape.isRequired,
        children: PropTypes.element.isRequired,
    }

  這里是要傳遞給子組件的數(shù)據(jù)類(lèi)型定義
    Provider.childContextTypes = {
        [storeKey]: storeShape.isRequired,
        [subscriptionKey]: subscriptionShape,
    }

    return Provider
}

ok 這個(gè)組件就分析到此 沒(méi)有什么可以太多講的 需要注意的就是context 但是一般我們不會(huì)接觸到這個(gè)

  • connect核心完整代碼
import connectAdvanced from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
import defaultMergePropsFactories from './mergeProps'
import defaultSelectorFactory from './selectorFactory'

/*
  connect is a facade over connectAdvanced. It turns its args into a compatible
  selectorFactory, which has the signature:

    (dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
  
  connect passes its args to connectAdvanced as options, which will in turn pass them to
  selectorFactory each time a Connect component instance is instantiated or hot reloaded.

  selectorFactory returns a final props selector from its mapStateToProps,
  mapStateToPropsFactories, mapDispatchToProps, mapDispatchToPropsFactories, mergeProps,
  mergePropsFactories, and pure args.

  The resulting final props selector is called by the Connect component instance whenever
  it receives new props or store state.
 */

function match(arg, factories, name) {
  for (let i = factories.length - 1; i >= 0; i--) {
    const result = factories[i](arg)
    if (result) return result
  }

  return (dispatch, options) => {
    throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
  }
}

function strictEqual(a, b) { return a === b }

// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) {
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true,
      areStatesEqual = strictEqual,
      areOwnPropsEqual = shallowEqual,
      areStatePropsEqual = shallowEqual,
      areMergedPropsEqual = shallowEqual,
      ...extraOptions
    } = {}
  ) {
    const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
    const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

    return connectHOC(selectorFactory, {
      // used in error messages
      methodName: 'connect',

       // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: name => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions
    })
  }
}

export default createConnect()

  • createConnect部分
export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) 
export default createConnect()
import connect from './connect/connect'

先來(lái)看一下函數(shù)定義 從上面的函數(shù)定義可以看到 其實(shí)是有暴露createConnect來(lái)進(jìn)行使用的 只不過(guò)默認(rèn)使用的是不傳參的createConnect導(dǎo)致全部都走了默認(rèn)值 但是其實(shí) 如果你想做一些自定義設(shè)置的話(huà) 可以直接調(diào)用createConnect來(lái)實(shí)現(xiàn)

  • connect部分
  return function connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    {
      pure = true,
      areStatesEqual = strictEqual,
      areOwnPropsEqual = shallowEqual,
      areStatePropsEqual = shallowEqual,
      areMergedPropsEqual = shallowEqual,
      ...extraOptions
    } = {}
  ) 
這里其實(shí)就是我們平常在用的connect了 第四個(gè)參數(shù)一般用來(lái)傳入pure或者withRef來(lái)控制是否使用pure跟是否返回ref withRef等其他的參數(shù)都存在了extraOptions這個(gè)里面
  • initProps初始化部分
    const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
    const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
    const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

function match(arg, factories, name) {
  for (let i = factories.length - 1; i >= 0; i--) {
    const result = factories[i](arg)
    if (result) return result
  }

  return (dispatch, options) => {
    throw new Error(`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${options.wrappedComponentName}.`)
  }
}

這里match傳入的factories 為對(duì)應(yīng)的判斷條件類(lèi)型是一個(gè)數(shù)組 只要你的判斷條件中 有任意一條符合條件 就直接拋出 否則返回 一個(gè)錯(cuò)誤
> * initMapStateToProps部分
這個(gè)根據(jù)mapStateToProps是否為undefined分為兩部分
whenMapStateToPropsIsFunction 
whenMapStateToPropsIsMissing

initMapStateToProps

  • wrapMapToPropsConstant 部分詳解
export function wrapMapToPropsConstant(getConstant) {
  return function initConstantSelector(dispatch, options) {
    const constant = getConstant(dispatch, options)
    function constantSelector() { return constant }
    constantSelector.dependsOnOwnProps = false 
    return constantSelector
  }
}

wrapMapToPropsConstant(() => ({}))
從這個(gè)例子里面看 這里寫(xiě)這么多代碼其實(shí)是無(wú)意義的 但是 這個(gè)主要是為了留給后面使用
wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
  • wrapMapToPropsFunc 部分詳解
export function getDependsOnOwnProps(mapToProps) {
  return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)
    ? Boolean(mapToProps.dependsOnOwnProps)
    : mapToProps.length !== 1
}

// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
// this function wraps mapToProps in a proxy function which does several things:
// 
//  * Detects whether the mapToProps function being called depends on props, which
//    is used by selectorFactory to decide if it should reinvoke on props changes.
//    
//  * On first call, handles mapToProps if returns another function, and treats that
//    new function as the true mapToProps for subsequent calls.
//    
//  * On first call, verifies the first result is a plain object, in order to warn
//    the developer that their mapToProps function is not returning a valid result.
//    
export function wrapMapToPropsFunc(mapToProps, methodName) {
  return function initProxySelector(dispatch, { displayName }) {
    // 當(dāng)程序執(zhí)行到這里的時(shí)候 其實(shí)proxy并沒(méi)有被運(yùn)行 只是返回了一個(gè)proxy的聲明
    // 會(huì)在handleFirstCall被進(jìn)行調(diào)用來(lái)初始化
    // 因?yàn)閯偛懦绦蛞呀?jīng)走了一遍的關(guān)系 這里dependsOnOwnProps默認(rèn)會(huì)變成true
    // 所以會(huì)走下面的那個(gè)proxy.mapToProps
    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
      return proxy.dependsOnOwnProps
        ? proxy.mapToProps(stateOrDispatch, ownProps)
        : proxy.mapToProps(stateOrDispatch)
    }

    // allow detectFactoryAndVerify to get ownProps
    proxy.dependsOnOwnProps = true

    proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
      // 當(dāng)程序走到這里的時(shí)候 會(huì)將mapToProps進(jìn)行復(fù)制 這邊要進(jìn)行運(yùn)算了 只要mapToProps返回的內(nèi)容不為function 并且等于object的話(huà) 程序就直接結(jié)束了
      // 否則就會(huì)繼續(xù)賦值不斷的遍歷執(zhí)行 知道返回了object以后 才將最終的結(jié)果返回到最外層
      proxy.mapToProps = mapToProps
      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
      // 這里賦值以后 又會(huì)重新的走上面那個(gè)函數(shù) 但是因?yàn)楝F(xiàn)在用的是我們自己寫(xiě)的mapStateToProps函數(shù)了 所以不可能會(huì)有dependsOnOwnProps
      // 所以這邊的dependsOnOwnProps永遠(yuǎn)會(huì)是false 因?yàn)閒alse的關(guān)系 所以會(huì)直接調(diào)用mapStateToProps(stateOrDispatch)
      let props = proxy(stateOrDispatch, ownProps)

      if (typeof props === 'function') {
        // 只要返回的不是function并且等于object的話(huà) 這邊就結(jié)束了 直接返回新的數(shù)據(jù) 否則不停的深層執(zhí)行
        proxy.mapToProps = props
        proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
        props = proxy(stateOrDispatch, ownProps)
      }

      if (process.env.NODE_ENV !== 'production') 
        verifyPlainObject(props, displayName, methodName)

      return props
    }

    return proxy
  }
}
之前我這一部分也沒(méi)有完全看懂 這里的設(shè)計(jì)非常巧妙 正常情況下 我們的mapStateToProps返回的就是一個(gè)普通的對(duì)象 但是mapStateToProps其實(shí)支持返回一個(gè)function 那么問(wèn)題就來(lái)了 因?yàn)槊總€(gè)人的書(shū)寫(xiě)習(xí)慣不一致的關(guān)系 所以我在mapStateToProps返回了一個(gè)閉包以后 要如何正常獲取數(shù)據(jù)呢 這句代碼的用處就是不斷的遍歷直到你返回的數(shù)據(jù)為一個(gè)object才放行
  • initMapDispatchToProps部分

這里分成了三個(gè)部分 前面兩個(gè)部分 跟上面講的是一樣的就不多說(shuō)了 主要說(shuō)一下第三個(gè)部分
whenMapDispatchToPropsIsFunction = whenMapStateToPropsIsFunction
whenMapDispatchToPropsIsMissing = whenMapStateToPropsIsMissing
whenMapDispatchToPropsIsObject

  • whenMapDispatchToPropsIsObject 部分詳解
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
  return (mapDispatchToProps && typeof mapDispatchToProps === 'object')
    ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch))
    : undefined
}


export function wrapMapToPropsConstant(getConstant) {
  return function initConstantSelector(dispatch, options) {
    const constant = getConstant(dispatch, options)

    function constantSelector() { return constant }
    constantSelector.dependsOnOwnProps = false 
    return constantSelector
  }
}

說(shuō)明:
假設(shè)現(xiàn)在的mapDispatchToProps是這樣的一個(gè)結(jié)構(gòu)

{
x:function(data){
return {type:'xxx',data}
  }
}
經(jīng)過(guò)bindActionCreators以后就變成了
x:function(action){
  dispatch(x(action))
}
在每個(gè)function的前面都外包了一層 使得可以做到自動(dòng)發(fā)起action的功能

  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

  • initMergeProps
  • whenMergePropsIsOmitted 如果為undefined的時(shí)候 載入
export function whenMergePropsIsOmitted(mergeProps) {
  return (!mergeProps)
    ? () => defaultMergeProps
    : undefined
}

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}
這里不多說(shuō) 就是合并數(shù)據(jù)用的 
  • whenMergePropsIsFunction 如果mergeProps為function的時(shí)候
export function whenMergePropsIsFunction(mergeProps) {
  return (typeof mergeProps === 'function')
    ? wrapMergePropsFunc(mergeProps)
    : undefined
}

export function wrapMergePropsFunc(mergeProps) {
  return function initMergePropsProxy(
    dispatch, { displayName, pure, areMergedPropsEqual }
  ) {
    let hasRunOnce = false
    let mergedProps

    return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
      const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)

      if (hasRunOnce) {
        if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps))
          mergedProps = nextMergedProps

      } else {
        hasRunOnce = true
        mergedProps = nextMergedProps

        if (process.env.NODE_ENV !== 'production')
          verifyPlainObject(mergedProps, displayName, 'mergeProps')
      }

      return mergedProps
    }
  }
}
這部分后續(xù)補(bǔ)上 因?yàn)橛玫牟皇翘貏e的多
  • connectHOC 部分
    return connectHOC(selectorFactory, {
      // used in error messages
      methodName: 'connect',

       // used to compute Connect's displayName from the wrapped component's displayName.
      getDisplayName: name => `Connect(${name})`,

      // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
      shouldHandleStateChanges: Boolean(mapStateToProps),

      // passed through to selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      pure,
      areStatesEqual,
      areOwnPropsEqual,
      areStatePropsEqual,
      areMergedPropsEqual,

      // any extra options args can override defaults of connect or connectAdvanced
      ...extraOptions
    })

connectHOC中return了這個(gè)函數(shù) 這就是為什么我們的connect是
connect()()這樣調(diào)用的原因 這個(gè)一個(gè)高階組件 由function返回一個(gè)view
return function wrapWithConnect(WrappedComponent) 

先看一個(gè)參數(shù)部分
    const selectorFactoryOptions = {
      ...connectOptions,
      getDisplayName,
      methodName,
      renderCountProp,
      shouldHandleStateChanges,
      storeKey,
      withRef,
      displayName,
      wrappedComponentName,
      WrappedComponent
    }

this.initSelector()
this.initSubscription()
這兩行代碼 決定了整個(gè)的react-redux的運(yùn)行
  • initSelector
      initSelector() {
        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
        this.selector = makeSelectorStateful(sourceSelector, this.store)
        this.selector.run(this.props)
      }
selectorFactory這里的這個(gè)參數(shù)是由外部的createConnect時(shí)傳遞進(jìn)來(lái)的 看一下這個(gè)定義
  • selectorFactory
export default function finalPropsSelectorFactory(dispatch, {
  initMapStateToProps,
  initMapDispatchToProps,
  initMergeProps,
  ...options
}) {
  // 這里就會(huì)初始化wrapMapToPropsFunc中第一層的代碼 并且將里面的內(nèi)容返回繼續(xù)往下傳遞 具體可以看前面的說(shuō)明
  const mapStateToProps = initMapStateToProps(dispatch, options)
  const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  const mergeProps = initMergeProps(dispatch, options)

  if (process.env.NODE_ENV !== 'production') {
    verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName)
  }
  // 這里就比較關(guān)鍵了 如果你設(shè)置了pure以后 這邊走的其實(shí)是兩個(gè)完全不同的路線(xiàn)
  // 先優(yōu)先看一下比較簡(jiǎn)單的部分
  const selectorFactory = options.pure
    ? pureFinalPropsSelectorFactory
    : impureFinalPropsSelectorFactory

  return selectorFactory(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
    dispatch,
    options
  )
  • impureFinalPropsSelectorFactory
export function impureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch
) {
  return function impureFinalPropsSelector(state, ownProps) {
    return mergeProps(
      mapStateToProps(state, ownProps),
      mapDispatchToProps(dispatch, ownProps),
      ownProps
    )
  }
}
這個(gè)函數(shù)的作用 就是調(diào)用mergerProps并返回最新的nextProps數(shù)據(jù) 用于進(jìn)行比對(duì) 這是針對(duì)沒(méi)有pure屬性的 如果有pure屬性 就會(huì)相對(duì)復(fù)雜一點(diǎn)了
  • pureFinalPropsSelectorFactory
// 這里傳遞進(jìn)來(lái)的
// 實(shí)際上已經(jīng)是運(yùn)行了第一層代碼的返回函數(shù)了
// mapStateToProps,
// mapDispatchToProps,
// mergeProps,
export function pureFinalPropsSelectorFactory(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  dispatch,
  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
) {
  let hasRunAtLeastOnce = false
  let state
  let ownProps
  let stateProps
  let dispatchProps
  let mergedProps

  function handleFirstCall(firstState, firstOwnProps) {
    // 首次運(yùn)行就比較簡(jiǎn)單 因?yàn)椴恍枰ケ葘?duì)任何的數(shù)據(jù) 也沒(méi)有任何的數(shù)據(jù)可以比對(duì) 
    // 所以這里只要直接拿數(shù)據(jù)就可以了
    state = firstState
    ownProps = firstOwnProps
    stateProps = mapStateToProps(state, ownProps)
    dispatchProps = mapDispatchToProps(dispatch, ownProps)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    // 在這里將這個(gè)條件設(shè)置為真 下次就不會(huì)跳轉(zhuǎn)到首次運(yùn)行這邊了
    hasRunAtLeastOnce = true
    return mergedProps
  }

  function handleNewPropsAndNewState() {
    // 執(zhí)行返回state
    stateProps = mapStateToProps(state, ownProps)
    // 如果dependsOnOwnProps為true返回新的props否則使用原有的
    if (mapDispatchToProps.dependsOnOwnProps){
      dispatchProps = mapDispatchToProps(dispatch, ownProps)
    }
    // 合并所有數(shù)據(jù)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 合并新的props
  function handleNewProps() {
    // 這部分可以看wrapMapToPropsFunc部分的說(shuō)明 有詳細(xì)解釋
    // dependsOnOwnProps 正常情況 
    // 第一次執(zhí)行的時(shí)候 肯定是true 第二次是個(gè)fasle 因?yàn)楫?dāng)時(shí)的函數(shù)里面還沒(méi)有帶上這個(gè)屬性 
    // 第三次的話(huà)就是true了 這時(shí)候調(diào)用proxy的時(shí)候 這個(gè)屬性就有了
    // 當(dāng)你正常的返回了一個(gè)object以后 你就帶上了這個(gè)屬性
    // 那么當(dāng)你下次在進(jìn)行更新的時(shí)候就會(huì)直接獲取新的數(shù)據(jù)了
    if (mapStateToProps.dependsOnOwnProps)
      stateProps = mapStateToProps(state, ownProps)

    if (mapDispatchToProps.dependsOnOwnProps)
      dispatchProps = mapDispatchToProps(dispatch, ownProps)

    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
    return mergedProps
  }

  // 這里跟上面的一致
  function handleNewState() {
    const nextStateProps = mapStateToProps(state, ownProps)
    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
    stateProps = nextStateProps
    
    if (statePropsChanged)
      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

    return mergedProps
  }

  function handleSubsequentCalls(nextState, nextOwnProps) {
    // 比對(duì)props跟state是否被更改
    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
    const stateChanged = !areStatesEqual(nextState, state)
    state = nextState
    ownProps = nextOwnProps
    // 如果兩個(gè)都被更改就一起生成新的 否則就針對(duì)自身進(jìn)行生成
    if (propsChanged && stateChanged) return handleNewPropsAndNewState()
    if (propsChanged) return handleNewProps()
    if (stateChanged) return handleNewState()
    // 如果什么都沒(méi)有改變的話(huà) 則直接返回原有數(shù)據(jù)
    return mergedProps
  }
  // hasRunAtLeastOnce判斷是否是首次運(yùn)行 如果是首次運(yùn)行就跳轉(zhuǎn)到handleFirstCall 
  return function pureFinalPropsSelector(nextState, nextOwnProps) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}
  • 核心更新比對(duì)效驗(yàn)的部分
    areStatesEqual = strictEqual,
    areOwnPropsEqual = shallowEqual,
    areStatePropsEqual = shallowEqual,
    areMergedPropsEqual = shallowEqual,
    從這里可以看到 基本上所有的更新 都是走shallowEqual這個(gè)的 針對(duì)這個(gè)來(lái)做一下分析
export default function shallowEqual(objA, objB) {
  // 先判斷兩個(gè)對(duì)象是否一致 如果一致則返回true 認(rèn)為不需要更新
  if (is(objA, objB)) return true
  // 只要滿(mǎn)足null 或者 !== object 這兩個(gè)條件的任意一個(gè) 都返回false 認(rèn)為需要更新
  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false
  }

  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)
  // 如果A B 是兩個(gè)對(duì)象 keys以后的長(zhǎng)度不一致 也返回false 認(rèn)為下需要更新
  if (keysA.length !== keysB.length) return false
  // 如果兩者長(zhǎng)度一致 在依次比對(duì)內(nèi)部數(shù)據(jù) 看是否一致
  for (let i = 0; i < keysA.length; i++) {
    // 如果objB里面不存在keysA屬性的話(huà) 直接返回fasle認(rèn)為需要更新了
    // 或者如果相同數(shù)組下objA與objB不相等的話(huà) 也認(rèn)為需要更新
    if (!hasOwn.call(objB, keysA[i]) ||
        !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }
  // 除此情況外 都認(rèn)為不需要更新
  return true
}

更新邏輯
其中sourceSelector這個(gè) 就是上面剛講到的那部分 每次執(zhí)行返回一個(gè)最新的nextProps并且還會(huì)在跟自身原有的selector.props比對(duì) 如果不一致就將
selector.shouldComponentUpdate = true
然后在后面步驟去參與更新

constructor  =>initSelector =>makeSelectorStateful
先在構(gòu)造函數(shù)中執(zhí)行這個(gè)進(jìn)行初始化
        this.initSelector()
        this.initSubscription()

之后在initSelector中對(duì)于數(shù)據(jù)進(jìn)行初始化 這里很關(guān)鍵
        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
        this.selector = makeSelectorStateful(sourceSelector, this.store)
        this.selector.run(this.props)

這里負(fù)責(zé)比對(duì)數(shù)據(jù)sourceSelector就是上面講到的 會(huì)走對(duì)應(yīng)閉包里面的函數(shù) 用于返回一個(gè)最新的nextProps然后跟當(dāng)前的selector.props比對(duì) 如果不一樣的話(huà)就返回一個(gè)最新的 如果一樣就處理 也就是 一個(gè)false
selector.shouldComponentUpdate 這個(gè)會(huì)在每次render的時(shí)候 被設(shè)置為false
function makeSelectorStateful(sourceSelector, store) {
  // wrap the selector in an object that tracks its results between runs.
  const selector = {
    run: function runComponentSelector(props) {
      try {
        const nextProps = sourceSelector(store.getState(), props)
        if (nextProps !== selector.props || selector.error) {
          selector.shouldComponentUpdate = true
          selector.props = nextProps
          selector.error = null
        }
      } catch (error) {
        selector.shouldComponentUpdate = true
        selector.error = error
      }
    }
  }

  return selector
}
  • 接下來(lái)講一下Subscription監(jiān)聽(tīng)的部分
    先來(lái)看一下在程序中的調(diào)用順序
        this.initSelector()
        this.initSubscription()

      initSubscription() {
        if (!shouldHandleStateChanges) return

        // parentSub's source should match where store came from: props vs. context. A component
        // connected to the store via props shouldn't use subscription from context, or vice versa.
        // 這里有一點(diǎn)需要注意的就是propsMode 如果有數(shù)據(jù)的話(huà) 會(huì)從props取 沒(méi)有則從context取 正常情況下
        // 簡(jiǎn)單來(lái)說(shuō) 我們知道context會(huì)被繼承下去 如果這里有獲取到數(shù)據(jù)的話(huà) 就會(huì)直接使用原有的 而不是現(xiàn)有的
        // 并不是每次每個(gè)組件都使用了一個(gè)新的監(jiān)聽(tīng) 而是從父級(jí)開(kāi)始 如果父有了一個(gè)監(jiān)聽(tīng) 就使用父的 當(dāng)我這邊去進(jìn)行改變的時(shí)候
        // 默認(rèn)其實(shí)會(huì)觸發(fā)父的監(jiān)聽(tīng) 在由父來(lái)回調(diào)當(dāng)前頁(yè)面的監(jiān)聽(tīng)函數(shù) 做到盡量少的創(chuàng)建一個(gè)監(jiān)聽(tīng) 實(shí)現(xiàn)公用
        const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
        this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))

        // `notifyNestedSubs` is duplicated to handle the case where the component is  unmounted in
        // the middle of the notification loop, where `this.subscription` will then be null. An
        // extra null check every change can be avoided by copying the method onto `this` and then
        // replacing it with a no-op on unmount. This can probably be avoided if Subscription's
        // listeners logic is changed to not call listeners that have been unsubscribed in the
        // middle of the notification loop.
        // 這里綁定監(jiān)聽(tīng)需要用到的函數(shù)
        // 當(dāng)你第一次執(zhí)行的時(shí)候 因?yàn)?        // const nullListeners = { notify() {} }
        // this.listeners = nullListeners
        // 這個(gè)時(shí)候直接執(zhí)行是不會(huì)有任何效果的
        this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)


      }
    第一次初始化會(huì)在didMount以后被觸發(fā)
      componentDidMount() {
        if (!shouldHandleStateChanges) return

        // componentWillMount fires during server side rendering, but componentDidMount and
        // componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
        // Otherwise, unsubscription would never take place during SSR, causing a memory leak.
        // To handle the case where a child component may have triggered a state change by
        // dispatching an action in its componentWillMount, we have to re-run the select and maybe
        // re-render.
        // 在這里執(zhí)行第一次的監(jiān)聽(tīng) 這里僅僅還只是加入隊(duì)列中 并沒(méi)有執(zhí)行
        this.subscription.trySubscribe()
        // 這里計(jì)算一次返回最新的this.selector.shouldComponentUpdate
        this.selector.run(this.props)
        // 如果當(dāng)前需要更新的話(huà) 采取強(qiáng)制更新
        if (this.selector.shouldComponentUpdate) this.forceUpdate()
      }

  trySubscribe() {
    // 當(dāng)你執(zhí)行了這個(gè)函數(shù)以后 
    if (!this.unsubscribe) {
      this.unsubscribe = this.parentSub
        // 
        ? this.parentSub.addNestedSub(this.onStateChange)
        // 這里使用的是redux中提供的函數(shù) 在這里當(dāng)你每次dispatch的時(shí)候 就會(huì)被觸發(fā)到
        : this.store.subscribe(this.onStateChange)
      // 當(dāng)你走到這里的時(shí)候 listeners就被替換了 這個(gè)時(shí)候 才是真正可以使用監(jiān)聽(tīng)了
      // 如果你不是從parent獲取更新的話(huà) 默認(rèn)這里使用redux來(lái)維護(hù)你的監(jiān)聽(tīng)
      // 但是如果你有parent以后 是由內(nèi)部來(lái)進(jìn)行維護(hù)的
      // 內(nèi)部調(diào)用的時(shí)候 其實(shí)會(huì)繼續(xù)走addNestedSub
      // this.listeners.subscribe(listener)
      // 并且在這里返回一個(gè)監(jiān)聽(tīng)
      // 這個(gè)定義的變量也只會(huì)在有父級(jí)的時(shí)候 被使用到 因?yàn)樽陨硪灿锌赡艹蔀閯e人的父 
      // 所以這邊沒(méi)有做其他的判斷 采取全部都定義的方式
      this.listeners = createListenerCollection()
    }
  }

ok 到這里為止 綁定就都完成了 剩下的就是等redux的store改變的時(shí)候 觸發(fā)更新了 來(lái)看一下更新部分的邏輯
  • createListenerCollection部分
function createListenerCollection() {
 // the current/next pattern is copied from redux's createStore code.
 // TODO: refactor+expose that code to be reusable here?
 let current = []
 let next = []

 return {
   // 清楚當(dāng)前隊(duì)列
   clear() {
     next = CLEARED
     current = CLEARED
   },

   //這里要注意 如果當(dāng)前下面沒(méi)有子的話(huà) 其實(shí)這里的代碼是無(wú)意義的 
   // 只有自身成為了父 并且有子視圖掛在下面這里才會(huì)執(zhí)行隊(duì)列 
   notify() {
     const listeners = current = next
     for (let i = 0; i < listeners.length; i++) {
       listeners[i]()
     }
   },

   // 獲取當(dāng)前的最新監(jiān)聽(tīng)數(shù)據(jù)
   get() {
     return next
   },

   subscribe(listener) {
     let isSubscribed = true
     // 如果不相等就將當(dāng)前的監(jiān)聽(tīng)添加到數(shù)組中
     if (next === current) next = current.slice()
     next.push(listener)
     // 這里模仿了redux返回了一個(gè)卸載監(jiān)聽(tīng)的函數(shù)
     return function unsubscribe() {
       if (!isSubscribed || current === CLEARED) return
       isSubscribed = false

       if (next === current) next = current.slice()
       next.splice(next.indexOf(listener), 1)
     }
   }
 }
}
  • react-redux render部分代碼
      addExtraProps(props) {
        if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
        // make a shallow copy so that fields added don't leak to the original selector.
        // this is especially important for 'ref' since that's a reference back to the component
        // instance. a singleton memoized selector would then be holding a reference to the
        // instance, preventing the instance from being garbage collected, and that would be bad
        const withExtras = { ...props }
        if (withRef) withExtras.ref = this.setWrappedInstance
        if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
        if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
        return withExtras
      }

      render() {
        const selector = this.selector
        selector.shouldComponentUpdate = false

        if (selector.error) {
          throw selector.error
        } else {
          return createElement(WrappedComponent, this.addExtraProps(selector.props))
        }
      }
這里比較簡(jiǎn)單就是將參數(shù)復(fù)制過(guò)去而已

react-redux所有代碼注釋地址:https://github.com/fangkyi03/react-redux.git
ok react-redux所有代碼 到此全部玩不

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末魁瞪,一起剝皮案震驚了整個(gè)濱河市沛膳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堰氓,老刑警劉巖蝙砌,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阵漏,死亡現(xiàn)場(chǎng)離奇詭異攀芯,居然都是意外死亡好渠,警方通過(guò)查閱死者的電腦和手機(jī)弦撩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)步咪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人益楼,你說(shuō)我怎么就攤上這事猾漫。” “怎么了感凤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵悯周,是天一觀的道長(zhǎng)陪竿。 經(jīng)常有香客問(wèn)我禽翼,道長(zhǎng),這世上最難降的妖魔是什么族跛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任闰挡,我火速辦了婚禮,結(jié)果婚禮上礁哄,老公的妹妹穿的比我還像新娘长酗。我一直安慰自己,他們只是感情好桐绒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布花枫。 她就那樣靜靜地躺著刻盐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劳翰。 梳的紋絲不亂的頭發(fā)上敦锌,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音佳簸,去河邊找鬼乙墙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛生均,可吹牛的內(nèi)容都是我干的听想。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼马胧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼汉买!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起佩脊,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛙粘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后威彰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體出牧,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年歇盼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舔痕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豹缀,死狀恐怖伯复,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邢笙,我是刑警寧澤边翼,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鸣剪,受9級(jí)特大地震影響组底,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筐骇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一债鸡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铛纬,春花似錦厌均、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晶密。三九已至,卻和暖如春模她,著一層夾襖步出監(jiān)牢的瞬間稻艰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工侈净, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尊勿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓畜侦,卻偏偏與公主長(zhǎng)得像元扔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子旋膳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容