博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React-redux源码分析
阅读量:6813 次
发布时间:2019-06-26

本文共 15200 字,大约阅读时间需要 50 分钟。

React-redux源码总共分为两部分,第一是Provider,第二是connect

Provider

Provider比较简单,主要代码如下:

class Provider extends Component {        getChildContext() {          return { [storeKey]: this[storeKey], [subscriptionKey]: null }        }        constructor(props, context) {          super(props, context)          this[storeKey] = props.store;        }        render() {          return Children.only(this.props.children)        }    }复制代码

即通过context api将store传到子组件里面去。

connect

connect算是一个比较有难度的部分,函数原型是:

connect(mapStateToProps,mapDispatchToProps,[mergeProps]) => (WrappedComponent) => HOC复制代码

我们尝试对其进行由上到下的分析:

入口

函数入口就是一个三阶函数:

export function createConnect({    connectHOC,    mapStateToPropsFactories,    mapDispatchTpropsFactories,    mergePropsFactories,    selectFactory}) => connect(mapStateToProps,mapDispatchToProps,mergeProps, {
/*...*/}) => { const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps') const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps') const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps') return connectHOC(selectorFactory, { initMapStateToProps, initMapDispatchToProps, initMergeProps, //... })}export default createConnect()复制代码

写的其实比较绕,但是理解起来不难,首先createConnect方法只相当于一个包装而已,将高阶组件connectHOC,以及一些工厂函数引入到connect的父scope中来。

然后在connect中,通过match函数来选择适当的factory来包装外界传入的参数)生成三个函数: initMapStateToProps,initMapDispatchToPropsinitMergeProps

match函数的源码:

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}.`) }}复制代码

这段代码其中arg就是connect函数接收的mapStateToProps等,以倒序的方式寻找第一个返回非空的factory。我们可以先暂且不去深入推敲。

ConnectHOC

connectHOC是整个react-redux的核心,在分析源代码之前,我们先看看一个工具函数:

function makeSelectorStateful(sourceSelector, store) {  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}复制代码

selector主要作用是对redux的state和dispatch与react的props进行合并(mergeProps) 这段代码主要是对sourceSelector进行封装,增加了错误处理,新旧值的比较等。

下面是connectHOC的主要源码:

// ...export default function connectAdvanced(  selectorFactory,  // options object:  {    //省略掉的参数: withRef,displayName,renderCountProp,shouldHandleStateChanges,storeKey,getDisplayName    // 传入selectorFactory中额外的参数    // 主要包括 `initMapStateToProps`,`initMapDispatchToProps`和`initMergeProps`    ...connectOptions  } = {}) {  const subscriptionKey = storeKey + 'Subscription'  // ...  return function wrapWithConnect(WrappedComponent) {    // ...     const selectorFactoryOptions = {      ...connectOptions,      //...       WrappedComponent    }        // 声明一个React组件    class Connect extends Component {      constructor(props, context) {        super(props, context)        // ...        // 从props或者context里面获得redux store        this.store = props[storeKey] || context[storeKey]        this.propsMode = Boolean(props[storeKey])        this.initSelector()        this.initSubscription()      }            initSelector() {        // 结合mapPropsTo...和mergeProps函数处理参数。(state, props) => nextProps        const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)        // makeSelectorStateful:  包装sourceSelector函数,比较props和nextProps确定shouldComponentUpdate        this.selector = makeSelectorStateful(sourceSelector, this.store)        // 更新当前参数,实际上就是ownProps        this.selector.run(this.props)      }      initSubscription() {        // 如果不需要相应redux state变化,则不需要订阅        if (!shouldHandleStateChanges) return        // 获取祖先传下来的订阅对象        const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]                // 生成当前组件的订阅对象,传入parentSub。        // 当parentSub不为空的时候,onStateChange实际上是注册到了parentSub的listeners数组,由组件手动调用notifyNestedSubs执行。        // 否则直接使用store.subscribe注册到redux中,由redux控制执行        // 这样做能始终保证祖先节点的更新早于子孙节点        this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))        this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)      }      onStateChange() {        // 更新props        this.selector.run(this.props)        if (!this.selector.shouldComponentUpdate) {          // 如果当前组件不需要更新,那么直接更新子组件          this.notifyNestedSubs()        } else {         // 先更新当前组件,再更新子组件          this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate          // dummyState 为空,意味触发react的更新机制          this.setState(dummyState)        }      }      getChildContext() {        // 如果store是从props中获得,那么直接将祖先的subscription对象传下去。(仅作为一个传话人,不响应父组件的dispatch事件)        // 否则,将自身的subscription传下去,形状如下        // Component A (B's subscription's parentSub == A)        // |        //  -> Component B (C's subscription's parentSub == B)        //     |        //      -> Component C (E's subscription's parentSub == C)        //     |        //     |== Component D (propsMode === true) directly transfer C's sub context to E        //         |        //          -> Component E        // when a dispatch occured in Component A,the callback exec order will be A -> B -> C -> E                const subscription = this.propsMode ? null : this.subscription        return { [subscriptionKey]: subscription || this.context[subscriptionKey] }      }      componentDidMount() {        if (!shouldHandleStateChanges) return        // 因为componentWillMount在ssr中执行,而componentDidMount和componentWillUnmount不执行。因此如果将trySubscribe方法放在componentWillMount中的话,在ssr中可能会因为没有调用componentWillUnmount的unsubscribe而内存泄漏        this.subscription.trySubscribe()                // run即update,更新selector.nextProps,selector.shouldComponentUpdate        this.selector.run(this.props)                // 如果子组件在componentWillMount的时候dispatch了一个action,因而没有被上文的subscription捕获到,此时的selector就会检测出参数的不一致(shouldComponentUpdate = true),所以需要强制更新        if (this.selector.shouldComponentUpdate) this.forceUpdate()      }      // 更新props      componentWillReceiveProps(nextProps) {        this.selector.run(nextProps)      }      shouldComponentUpdate() {        return this.selector.shouldComponentUpdate      }      componentWillUnmount() {        if (this.subscription) this.subscription.tryUnsubscribe()                // 为什么直接使用subscription.notifyNestedSubs而要拷贝出来分别处理:        // 防止在notify过程中subscription为null,而null.notifyNestedSubs报错        this.subscription = null        this.notifyNestedSubs = noop                this.store = null        this.selector.run = noop        this.selector.shouldComponentUpdate = false      }      notifyNestedSubsOnComponentDidUpdate() {        // 避免多次调用        this.componentDidUpdate = undefined        this.notifyNestedSubs()      }      isSubscribed() {        return Boolean(this.subscription) && this.subscription.isSubscribed()      }      addExtraProps(props) {        // 如果没有额外的参数,直接返回props        if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props        // 防御性拷贝        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))        }      }    }    // ...        // 仅在开发环境下支持的的热重载    if (process.env.NODE_ENV !== 'production') {      Connect.prototype.componentWillUpdate = function componentWillUpdate() {        // We are hot reloading!        if (this.version !== version) {          this.version = version          this.initSelector()                    let oldListeners = [];          if (this.subscription) {            oldListeners = this.subscription.listeners.get()            this.subscription.tryUnsubscribe()          }          this.initSubscription()          if (shouldHandleStateChanges) {            this.subscription.trySubscribe()            oldListeners.forEach(listener => this.subscription.listeners.subscribe(listener))          }        }      }    }   // 将WrappedComponent中的非React特定的静态属性(例如propTypes就是React的特定静态属性)赋值到Connect。   // 作用有点类似于Object.assign,但是仅复制非React特定的静态属性。    return hoistStatics(Connect, WrappedComponent)  }}复制代码

至此,大部分逻辑我们已经很清楚了。现在我们来分析一下各种factory所做的事情:

selectorFactory

首先看主体:

export default function finalPropsSelectorFactory(dispatch, {  initMapStateToProps,  initMapDispatchToProps,  initMergeProps,  ...options}) {  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)  }  const selectorFactory = options.pure    ? pureFinalPropsSelectorFactory    : impureFinalPropsSelectorFactory  return selectorFactory(    mapStateToProps,    mapDispatchToProps,    mergeProps,    dispatch,    options  )}复制代码

该函数相当于一个控制器,根据pure选项来决定使用那种具体实现。 如果puretrue,则selectorFactory将会返回一个可以记忆结果的selector,这样才有可能使得connectHOC里面的selector.shouldComponentUpdatefalse。反之,selector将永远返回新的对象,selector.shouldComponentUpdate始终为true

impureFinalPropsSelectorFactory

export function impureFinalPropsSelectorFactory(  mapStateToProps,  mapDispatchToProps,  mergeProps,  dispatch) {  return function impureFinalPropsSelector(state, ownProps) {    return mergeProps(      mapStateToProps(state, ownProps),      mapDispatchToProps(dispatch, ownProps),      ownProps    )  }}复制代码

直接就很粗暴了

pureFinalPropsSelectorFactory

export function pureFinalPropsSelectorFactory(  mapStateToProps,  mapDispatchToProps,  mergeProps,  dispatch,  { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }) {// 闭包,memory之前的结果,仅当有变化的时候才更新  let hasRunAtLeastOnce = false  let state  let ownProps  let stateProps  let dispatchProps  // 合并的结果  let mergedProps    // 第一次调用  function handleFirstCall(firstState, firstOwnProps) {    state = firstState    ownProps = firstOwnProps    stateProps = mapStateToProps(state, ownProps)    dispatchProps = mapDispatchToProps(dispatch, ownProps)    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)    hasRunAtLeastOnce = true    return mergedProps  }  function handleNewPropsAndNewState() {    // state 已经 changed    stateProps = mapStateToProps(state, ownProps)    if (mapDispatchToProps.dependsOnOwnProps)      dispatchProps = mapDispatchToProps(dispatch, ownProps)    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)    return mergedProps  }  function handleNewProps() {    // 仅当state 用到了OwnProps才更新    if (mapStateToProps.dependsOnOwnProps)      stateProps = mapStateToProps(state, ownProps)    // 仅当disaptch 用到了OwnProps才更新    if (mapDispatchToProps.dependsOnOwnProps)      dispatchProps = mapDispatchToProps(dispatch, ownProps)              mergedProps = mergeProps(stateProps, dispatchProps, ownProps)    return mergedProps  }  function handleNewState() {    const nextStateProps = mapStateToProps(state, ownProps)    // shallow compare state是否改变    const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)    stateProps = nextStateProps        if (statePropsChanged)      mergedProps = mergeProps(stateProps, dispatchProps, ownProps)    return mergedProps  }  // 非首次调用  function handleSubsequentCalls(nextState, nextOwnProps) {    const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)    const stateChanged = !areStatesEqual(nextState, state)    state = nextState    ownProps = nextOwnProps        if (propsChanged && stateChanged) return handleNewPropsAndNewState()    if (propsChanged) return handleNewProps()    if (stateChanged) return handleNewState()    return mergedProps  }  return function pureFinalPropsSelector(nextState, nextOwnProps) {    return hasRunAtLeastOnce      ? handleSubsequentCalls(nextState, nextOwnProps)      : handleFirstCall(nextState, nextOwnProps)  }}复制代码

看上去复杂许多,但主要还是因为需要区分是否是首次调用而已。

mapStateToProps

mapStateToPropsmapDispatchToProps差不多,大部分都是公共代码(wrapMapToProps),因此我们以mapStateToProps为例:

import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'export function whenMapStateToPropsIsFunction(mapStateToProps) {  return (typeof mapStateToProps === 'function')    ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')    : undefined}export function whenMapStateToPropsIsMissing(mapStateToProps) {  return (!mapStateToProps)    ? wrapMapToPropsConstant(() => ({}))    : undefined}export default [  whenMapStateToPropsIsFunction,  whenMapStateToPropsIsMissing]复制代码

跟selectorFactory一样,这里同样有多种解决方案,不过不同的是,这里是直接将所有方案以数组的方式返回,供前面connect中的match函数匹配。

然后我们主要来看一下其中涉及到的几个函数:

wrapMapToPropsConstant

export function wrapMapToPropsConstant(getConstant) {  return function initConstantSelector(dispatch, options) {    const constant = getConstant(dispatch, options)    function constantSelector() { return constant }    constantSelector.dependsOnOwnProps = false     return constantSelector  }}复制代码

顾名思义,constant不允许结果发生变化的,所以函数里面调用传入的getConstant函数之后,直接返回一个getter函数:contantSelector

wrapMapToPropsFunc

// methodName只用来debugexport function wrapMapToPropsFunc(mapToProps, methodName) {  // options 只需要 displayname , 也只用来debug  return function initProxySelector(dispatch, { displayName }) {    const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {      return proxy.dependsOnOwnProps        ? proxy.mapToProps(stateOrDispatch, ownProps)        : proxy.mapToProps(stateOrDispatch)    }    proxy.dependsOnOwnProps = true    proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {      proxy.mapToProps = mapToProps      proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)      let props = proxy(stateOrDispatch, ownProps)      // 如果mapToProps执行后返回的结果还是一个函数,则继续递归调用,直到它扁平为止      if (typeof props === 'function') {        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  }}复制代码

这里主要运用了代理的方法,挺巧妙的对mapToProps方法进行了包装,增加了dependsOnOwnProps这个静态成员,判断方法很简单,直接判断Function的参数长度即可:

getDependsOnOwnProps

export function getDependsOnOwnProps(mapToProps) {  return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)    ? Boolean(mapToProps.dependsOnOwnProps)    : mapToProps.length !== 1}复制代码

至此,所有代码都通了, cheers!~

参考

转载地址:http://rvzzl.baihongyu.com/

你可能感兴趣的文章
springmvc拦截器
查看>>
三篇文章了解 TiDB 技术内幕 —— 谈调度
查看>>
JsonSort 对json排序
查看>>
MySQL常识篇
查看>>
一起学Java7新功能扩展——深入历险分享(一)
查看>>
关于parentNode和firstChild的一些坑
查看>>
JSP六种标准动作
查看>>
HTML DOM 节点
查看>>
hadoop两大核心之一:MapReduce总结
查看>>
JDBC & MySql - datetime类型字段的NULL值处理
查看>>
Redis 集群常用命令
查看>>
sql 更新数据
查看>>
java LinkedList简单运用
查看>>
Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
查看>>
常用正则表达式列表
查看>>
github中的watch、star、fork区别
查看>>
《Java数据结构和算法》Six 递归
查看>>
布尔短路
查看>>
神奇的AOP
查看>>
IO 】序列化与反序列化
查看>>