注意:文章很长,只想了解逻辑而不深入的,可以直接跳到总结部分


初识

首先,从它暴露对外的API开始


  1. ReactReduxContext
  2. /*
  3. 提供了 React.createContext(null)
  4. */
  5. Provider
  6. /*
  7. 一个储存数据的组件,渲染了ContextProvider,内部调用redux中store.subscribe
  8. 订阅数据,每当redux中的数据变动,比较新值与旧值,判断是否重新渲染
  9. */
  10. connect
  11. /*
  12. 一个高阶组件,第一阶传入对数据处理方法,第二阶传入要渲染的组件
  13. 内部处理了:
  14. 1. 对参数的检查
  15. 2. 对传入的数据处理方法进行处理
  16. (没传怎么处理,传了提供什么参数,传的类型不同怎么处理,结果如何比较等等)
  17. 3. 静态方法转移
  18. 4. 对渲染组件的传递(传递给connectAdvanced)
  19. */
  20. connectAdvanced
  21. /*
  22. 保存每一次执行的数据,执行connect定义的方案和逻辑,新旧数据对比(全等对比),渲染组件
  23. 这里作为公开API,如果我们去使用,那么connect里面的逻辑就需要我们自定义了。
  24. */

现在对它的大概工作范围有了解后,我们可以开始沿着执行顺序分析。


抽丝

Provider

我们使用时,当写完了redux的reducer, action, bindActionCreators, combineReducers, createStore这一系列内容后,
我们得到了一个store

会先使用<Provider store={store}包裹住根组件。

这时,Provider组件开始工作


  1. componentDidMount() {
  2. this._isMounted = true
  3. this.subscribe()
  4. }

第一次加载,需要执行subscribe

subscribe是什么呢,就是对reduxstore执行subscribe一个自定义函数,
这样,每当数据变动,这个函数便会执行


  1. subscribe() {
  2. const { store } = this.props
  3. // redux 的 store 订阅
  4. // 订阅后,每当state改变 则自动执行这个函数
  5. this.unsubscribe = store.subscribe(() =&gt; {
  6. // store.getState() 获取最新的 state
  7. const newStoreState = store.getState()
  8. // 组件未加载,取消
  9. if (!this._isMounted) {
  10. return
  11. }
  12. // 比较state是否相等,全等的不更新
  13. this.setState(providerState =&gt; {
  14. if (providerState.storeState === newStoreState) {
  15. return null
  16. }
  17. return { storeState: newStoreState }
  18. })
  19. })
  20. /* ... */
  21. }

看到吗,这个自定义函数非常简单,每次收到数据,进行全等比较,不等则更新数据。

这个组件的另2个生命周期函数:


  1. componentWillUnmount() {
  2. if (this.unsubscribe) this.unsubscribe()
  3. this._isMounted = false
  4. }
  5. componentDidUpdate(prevProps) {
  6. // 比较store是否相等,如果相等则跳过
  7. if (this.props.store !== prevProps.store) {
  8. // 取消订阅之前的,再订阅现在的(因为数据(store)不同了)
  9. if (this.unsubscribe) this.unsubscribe()
  10. this.subscribe()
  11. }
  12. }

这2段的意思就是,每当数据变了,就取消上一次数据的订阅,在订阅本次的数据,
当要销毁组件,取消订阅。

一段题外话(可跳过):

这个逻辑用HooksuseEffect简直完美匹配!


  1. useEffect(()=&gt;{
  2. subscribe()
  3. return ()=&gt;{
  4. unSubscribe()
  5. }
  6. },props.data)

这段的意思就是,当props.data发生改变,执行unSubscribe(),再执行subscribe()

逻辑完全一致有没有!

最后的render

这里Context就是React.createContext(null)


  1. &lt;Context.Provider value={this.state}&gt;
  2. {this.props.children}
  3. &lt;/Context.Provider&gt;

到这里我称为react-redux的第一阶段。

一个小总结,第一阶段就做了1件事:

定义了Provider组件,内部订阅了store


connect

到主菜了,先看它的export

export default createConnect()

一看,我们应该有个猜测,这货createConnect是个高阶函数。

看看它的参数吧。


  1. export function createConnect({
  2. connectHOC = connectAdvanced,
  3. mapStateToPropsFactories = defaultMapStateToPropsFactories,
  4. mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  5. mergePropsFactories = defaultMergePropsFactories,
  6. selectorFactory = defaultSelectorFactory
  7. } = {}) {
  8. /* ... */
  9. }

题外话:一个编写默认对象内部含有默认值的方法


  1. function a({x=1,y=2}={}){}
  2. a() // x:1,y:2
  3. a({}) // x:1,y:2
  4. a({x:2,z:5}) //x:2,y:2

这里先说明一下它的参数,后面读起来会很顺。


  1. connectHOC: 一个重要组件,用于执行已确定的逻辑,渲染最终组件,后面会详细说。
  2. mapStateToPropsFactories: mapStateToProps 这个传入的参数的类型选择一个合适的方法。
  3. mapDispatchToPropsFactories: mapDispatchToProps 这个传入的参数的类型选择一个合适的方法。
  4. mergePropsFactories: mergeProps 这个传入的参数的类型选择一个合适的方法。
  5. selectorFactory: 以上3个只是简单的返回另一个合适的处理方法,它则执行这些处理方法,并且对结果定义了如何比较的逻辑。

可能有点绕,但react-redux就是这么一个个高阶函数组成的,selectorFactory后面会详细说。

首先我们再次确定这3个名字很长,实际很简单的函数(源码这里不放了)

mapStateToPropsFactories

mapDispatchToPropsFactories

mergePropsFactories

它们只是判断了参数是否存在,是什么类型,并且返回一个合适的处理方法,它们并没有任何处理逻辑。

  • 举个例子:

    const MyComponent=connect((state)=>state.articles})

    这里我只定义了mapStateToProps,并且是个function,那么mapStateToPropsFactories就会返回一个
    处理function的方法。

    我没有定义mapDispatchToProps,那么mapDispatchToPropsFactories检测不到参数,
    则会提供一个默认值dispatch => ({ dispatch }),返回一个处理非function(object)的方法。

那么处理逻辑是谁定义呢?

wrapMapToProps

wrapMapToProps.js这个文件内部做了以下事情:

  1. 定义了一个处理object的方法(简单的返回即可,因为最终目的就是要object)。
  2. 定义了一个处理函数高阶函数(执行2次)的方法,这个方法比上面的复杂在于它需要检测参数是否订阅了ownProps

检测方法很简单,就是检查参数的length(这里dependsOnOwnProps是上一次检查的结果,如果存在则不需要再次检查)


  1. export function getDependsOnOwnProps(mapToProps) {
  2. return mapToProps.dependsOnOwnProps !== null &amp;&amp;
  3. mapToProps.dependsOnOwnProps !== undefined
  4. ? Boolean(mapToProps.dependsOnOwnProps)
  5. : mapToProps.length !== 1
  6. }

回到connect,继续往下看


  1. export function createConnect({
  2. /* 上面所讲的参数 */
  3. } = {}) {
  4. return function connect(
  5. mapStateToProps,
  6. mapDispatchToProps,
  7. mergeProps,
  8. {
  9. pure = true,
  10. areStatesEqual = strictEqual,
  11. areOwnPropsEqual = shallowEqual,
  12. areStatePropsEqual = shallowEqual,
  13. areMergedPropsEqual = shallowEqual,
  14. ...extraOptions
  15. } = {}
  16. ) {
  17. /* ... */
  18. }
  19. }

已经到了我们传递参数的地方,前3个参数意思就不解释了,最后的参数options


  1. areStatesEqual = strictEqual, // ===比较
  2. areOwnPropsEqual = shallowEqual, // 浅比较
  3. areStatePropsEqual = shallowEqual, // 浅比较
  4. areMergedPropsEqual = shallowEqual, // 浅比较

它们用在selectorFactory这个比较数据结果的方法内部。

继续往下看


  1. export function createConnect({
  2. /* 上面已讲 */
  3. } = {}) {
  4. return function connect(
  5. /* 上面已讲 */
  6. ) {
  7. const initMapStateToProps = match(
  8. mapStateToProps,
  9. mapStateToPropsFactories,
  10. 'mapStateToProps'
  11. )
  12. const initMapDispatchToProps = match(
  13. mapDispatchToProps,
  14. mapDispatchToPropsFactories,
  15. 'mapDispatchToProps'
  16. )
  17. const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

这里定义了3个变量(函数),match的作用是什么?

mapStateToProps举例来说,

因为上面也说了,mapStateToPropsFactories里面有多个方法,需要找到一个适合mapStateToProps的,
match就是干这事了。

match方法内部遍历mapStateToPropsFactories所有的处理方法,任何一个方法能够匹配参数mapStateToProps,便被match捕获返回,
如果一个都找不到则报错提示参数配置错误。

现在这3个变量定义明确了,都是对应的参数的合适的处理方法。

至此,我们已经完成了第二阶段,

做个小总结,第二阶段做了哪些事:

  1. connect接收了对参数处理方案(3个...Factories)。
  2. connect接收了参数的结果比较方案(selectFactory)
  3. connect接收了参数(mapStateToProps,mapDispatchToProps,mergeProps,options)。
  4. 定义了比较方案(4个are...Equal,其实就是全等比较浅比较)。

前2个阶段都是定义阶段,接下来需要我们传入自定义组件,也就是最后一个阶段

connect(...)(Component)


接着看connect源码


  1. export function createConnect({
  2. /* 上面已讲 */
  3. } = {}) {
  4. return function connect(
  5. /* 上面已讲 */
  6. ) {
  7. /* 上面已讲 */
  8. return connectHOC(selectorFactory, {
  9. // 方法名称,用在错误提示信息
  10. methodName: 'connect',
  11. // 最终渲染的组件名称
  12. getDisplayName: name =&gt; `Connect(${name})`,
  13. shouldHandleStateChanges: Boolean(mapStateToProps),
  14. // 以下是传递给 selectFactory
  15. initMapStateToProps,
  16. initMapDispatchToProps,
  17. initMergeProps,
  18. pure,
  19. areStatesEqual,
  20. areOwnPropsEqual,
  21. areStatePropsEqual,
  22. areMergedPropsEqual,
  23. // any extra options args can override defaults of connect or connectAdvanced
  24. ...extraOptions
  25. })
  26. }
  27. }

这里执行了connectHOC(),传递了上面已经讲过的参数,而connectHOC = connectAdvanced

因此我们进入最后一个对外APIconnectAdvanced

connectAdvanced

connectAdvanced函数,之前也提过,就是一个执行、组件渲染和组件更新的地方。

它里面没有什么新概念,都是将我们上面讲到的参数进行调用,最后根据结果进行渲染新组件。

还是从源码开始


  1. export default function connectAdvanced(
  2. selectorFactory,
  3. {
  4. // 执行后作用于connect这个HOC组件名称
  5. getDisplayName = name =&gt; `ConnectAdvanced(${name})`,
  6. // 用于错误提示
  7. methodName = 'connectAdvanced',
  8. // 有REMOVED标志,这里不关注
  9. renderCountProp = undefined,
  10. // 确定connect这个HOC是否订阅state变动,好像已经没有用到了
  11. shouldHandleStateChanges = true,
  12. // 有REMOVED标志,这里不关注
  13. storeKey = 'store',
  14. // 有REMOVED标志,这里不关注
  15. withRef = false,
  16. // 是否通过 forwardRef 暴露出传入的Component的DOM
  17. forwardRef = false,
  18. // React的createContext
  19. context = ReactReduxContext,
  20. // 其余的(比较方法,参数处理方法等)将会传递给上面的 selectFactory
  21. ...connectOptions
  22. } = {}
  23. ) {
  24. /* ... */
  25. }

参数也没什么特别的,有一个forwardRef作用就是能获取到我们传入的Component的DOM。
这里也不深入。

接着看


  1. export default function connectAdvanced(
  2. /* 上面已讲 */
  3. ) {
  4. /* ...对参数的一些验证和提示哪些参数已经作废... */
  5. // 定义Context
  6. const Context = context
  7. return function wrapWithConnect(WrappedComponent) {
  8. /* ...检查 WrappedComponent 是否符合要求... */
  9. /* ...获取传入的WrappedComponent的名称... */
  10. /* ...通过WrappedComponent的名称计算出当前HOC的名称... */
  11. /* ...获取一些上面的参数(没有新的参数,都是之前见过的)... */
  12. // Component就是React.Component
  13. let OuterBaseComponent = Component
  14. let FinalWrappedComponent = WrappedComponent
  15. // 是否纯组件
  16. if (pure) {
  17. OuterBaseComponent = PureComponent
  18. }
  19. /* 定义 makeDerivedPropsSelector 方法,作用后面讲 */
  20. /* 定义 makeChildElementSelector 方法,作用后面讲 */
  21. /* 定义 Connect 组件,作用后面讲 */
  22. Connect.WrappedComponent = WrappedComponent
  23. Connect.displayName = displayName
  24. /* ...如果是forWardRef 为true的情况,此处不深入... */
  25. // 静态方法转换
  26. return hoistStatics(Connect, WrappedComponent)
  27. }
  28. }

这一段特别长,因此我将不太重要的直接用注释说明了它们在做什么,具体代码就不放了(不重要)。

并且定义了3个新东西,makeDerivedPropsSelectormakeChildElementSelector,Connect

先看最后一句hoistStatics就是hoist-non-react-statics,它的作用是将组件WrappedComponent的所有非React
静态方法传递到Connect内部。

那么最终它还是返回了一个Connect组件。

Connect组件

这个组件已经是我们写了完整connect(...)(Component)的返回值了,所以能确定,只要调用<Connect />,就能渲染出一个新的组件出来。

因此它的功能就是确定是否重复更新组件和确定到底更新什么?

看一个组件,从constructor看起


  1. class Connect extends OuterBaseComponent {
  2. constructor(props) {
  3. super(props)
  4. /* ...提示一些无用的参数...*/
  5. this.selectDerivedProps = makeDerivedPropsSelector()
  6. this.selectChildElement = makeChildElementSelector()
  7. this.renderWrappedComponent = this.renderWrappedComponent.bind(this)
  8. }
  9. /* ... */
  10. }

绑定了一个方法,看名字是render的意思,先不管它。

执行了2个函数。

Connect组件还没完,这里先放着,我们先看makeDerivedPropsSelectormakeChildElementSelector

makeDerivedPropsSelector


  1. function makeDerivedPropsSelector() {
  2. // 闭包储存上一次的执行结果
  3. let lastProps
  4. let lastState
  5. let lastDerivedProps
  6. let lastStore
  7. let sourceSelector
  8. return function selectDerivedProps(state, props, store) {
  9. // props和state都和之前相等 直接返回上一次的结果
  10. if (pure &amp;&amp; lastProps === props &amp;&amp; lastState === state) {
  11. return lastDerivedProps
  12. }
  13. // 当前store和lastStore不等,更新lastStore
  14. if (store !== lastStore) {
  15. lastStore = store
  16. // 终于调用 selectorFactory 了
  17. sourceSelector = selectorFactory(
  18. store.dispatch,
  19. selectorFactoryOptions
  20. )
  21. }
  22. // 更新数据
  23. lastProps = props
  24. lastState = state
  25. // 返回的就是最终的包含所有相应的 state 和 props 的结果
  26. const nextProps = sourceSelector(state, props)
  27. // 最终的比较
  28. if (lastDerivedProps === nextProps) {
  29. return lastDerivedProps
  30. }
  31. lastDerivedProps = nextProps
  32. return lastDerivedProps
  33. }
  34. }

大概的说,makeDerivedPropsSelector的执行,先判断了当前传入的props(组件的props)state(redux传入的state)
跟以前的是否全等,如果全等就不需要更新了;

如果不等,则调用了高阶函数selectFactory,并且获得最终数据,最后再判断最终数据和之前的最终数据是否全等。

为什么第一次判断了,还要判断第二次,而且都是===判断?

因为第一次获取的stateredux传入的,是整个APP的所有数据,它们不等说明有组件更新了,但不确定是否是当前组件;

第二次比较的是当前组件的最新数据和以前数据对比。

现在,我们知道selectFactory的作用是获取当前组件的的最新数据,深入源码看看。

selectFactory


  1. export default function finalPropsSelectorFactory(
  2. // redux store的store.dispatch
  3. dispatch,
  4. // 3种已经确定了的处理方法
  5. { initMapStateToProps, initMapDispatchToProps, initMergeProps, ...options }
  6. ) {
  7. // 返回一个针对用户传入的类型的解析函数
  8. // 例如 mapStateToProps 如果是function,那么就返回proxy,proxy可以判断是否需要ownProps,并且对高阶函数的 mapStateToProps 进行2次处理,
  9. // 最终确保返回一个plainObject,否则报错
  10. const mapStateToProps = initMapStateToProps(dispatch, options)
  11. const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  12. const mergeProps = initMergeProps(dispatch, options)
  13. if (process.env.NODE_ENV !== 'production') {
  14. verifySubselectors(
  15. mapStateToProps,
  16. mapDispatchToProps,
  17. mergeProps,
  18. options.displayName
  19. )
  20. }
  21. const selectorFactory = options.pure
  22. ? pureFinalPropsSelectorFactory
  23. : impureFinalPropsSelectorFactory
  24. // 默认pure问题true,因此执行 pureFinalPropsSelectorFactory(...)
  25. return selectorFactory(
  26. mapStateToProps,
  27. mapDispatchToProps,
  28. mergeProps,
  29. dispatch,
  30. options
  31. )
  32. }

参数就不说了,看注释。

以下3个,到底返回了什么,源码在wrapMapToProps.js上面也说过这个文件内部做了什么事情。


  1. const mapStateToProps = initMapStateToProps(dispatch, options)
  2. const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
  3. const mergeProps = initMergeProps(dispatch, options)

这3个调用返回的一个函数,名字叫proxy,这个proxy一旦调用,
就能返回经过mapStateToProps, mapDispatchToProps, mergeProps这3个参数处理过后的数据(plainObject)。

接下来:


  1. const selectorFactory = options.pure
  2. ? pureFinalPropsSelectorFactory
  3. : impureFinalPropsSelectorFactory
  4. // 默认pure问题true,因此执行 pureFinalPropsSelectorFactory(...)
  5. return selectorFactory(
  6. mapStateToProps,
  7. mapDispatchToProps,
  8. mergeProps,
  9. dispatch,
  10. options
  11. )

返回了selectorFactory的调用值,也就是pureFinalPropsSelectorFactory(pure默认为true)。

pureFinalPropsSelectorFactory,它的代码不少,但逻辑很明了,大方向就是对比数据。

这里关键的如何比较不列代码,只用注释讲明白它的逻辑。


  1. export function pureFinalPropsSelectorFactory(
  2. // 接受3个proxy方法
  3. mapStateToProps,
  4. mapDispatchToProps,
  5. mergeProps,
  6. dispatch,
  7. // 接受3个比较方法
  8. { areStatesEqual, areOwnPropsEqual, areStatePropsEqual }
  9. ) {
  10. /* ...定义变量保存之前的数据(闭包)... */
  11. function handleFirstCall(firstState, firstOwnProps) {
  12. /* ...定义第一次执行数据比较的方法,也就是简单的赋值给上面定义的闭包变量... */
  13. }
  14. function handleNewPropsAndNewState() {
  15. /* 当state和props都有变动时的处理方法 */
  16. }
  17. function handleNewProps() {
  18. /* 当state无变动,props有变动时的处理方法 */
  19. }
  20. function handleNewState() {
  21. /* 当state有变动,props无变动时的处理方法 */
  22. }
  23. // 后续数据比较的方法
  24. function handleSubsequentCalls(nextState, nextOwnProps) {
  25. // 浅比较
  26. const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
  27. // 全等比较
  28. const stateChanged = !areStatesEqual(nextState, state)
  29. // 更新数据
  30. state = nextState
  31. ownProps = nextOwnProps
  32. // 当发生不相等的3种情况(关键)
  33. if (propsChanged &amp;&amp; stateChanged) return handleNewPropsAndNewState()
  34. if (propsChanged) return handleNewProps()
  35. if (stateChanged) return handleNewState()
  36. // 比较都相等,直接返回旧值
  37. return mergedProps
  38. }
  39. return function pureFinalPropsSelector(nextState, nextOwnProps) {
  40. return hasRunAtLeastOnce
  41. ? handleSubsequentCalls(nextState, nextOwnProps)
  42. : handleFirstCall(nextState, nextOwnProps)
  43. }
  44. }

上面的闭包变量储存了上一次的数据,关键点就是当和这一次的数据比较后,如果处理更新。

react-redux将它分为3种情况

  • stateprops都相等。
  • state相等,props不等。
  • state不等,props相等。

  • 第一种:stateprops都相等

    • mapStateToProps(proxy):

      不管是否订阅ownProps,执行mapStateToProps, 因为state有变动。

    • mapDispatchToProps(proxy):

      只有订阅了ownProps,才会执行mapDispatchToProps,因为state变动与mapDispatchToProps无影响。

    • mergedProps(proxy):

      必定执行,将所有结果合并。

  • 第二种:state相等,props不等

    • mapStateToProps(proxy):

      只有订阅了ownProps,才会执行mapStateToProps, 因为state无变动。

    • mapDispatchToProps(proxy):

      只有订阅了ownProps,才会执行mapDispatchToProps,因为state变动与mapDispatchToProps无影响。

    • mergedProps(proxy):

      必定执行,将所有结果合并。

  • 第三种:state不等,props相等

    • mapStateToProps(proxy):

      不管是否订阅ownProps,执行mapStateToProps, 因为state有变动。

      注意,这里结果需要浅比较判断

      因为如果没有浅比较检查,而两者刚好浅比较相等
      那么最后也会认为返回一个新的props,也就是相当于重复更新了。

      之所以第一个stateprops都有变动的不需要浅比较检查,
      是因为如果props变了,则必须要更新组件。

    • mapDispatchToProps(proxy):

      不会执行,因为它只关注props

    • mergedProps(proxy):

      只有上面浅比较不等,才会执行。

makeDerivedPropsSelector的总结:

通过闭包管理数据,并且通过浅比较和全等比较判断是否需要更新组件数据。

makeChildElementSelector

makeChildElementSelector也是一个高阶函数,储存了之前的数据组件,并且判断与当前的判断。

这里是最终渲染组件的地方,因为需要判断一下刚才最终给出的数据是否需要去更新组件。

2个逻辑:

  1. 数据与之前不等(===),更新组件。
  2. forWardRef属性值与之前不等,更新组件。

否则,返回旧组件(不更新)。

继续回到Connect组件。

之后就是render


  1. render() {
  2. // React的createContext
  3. const ContextToUse = this.props.context || Context
  4. return (
  5. &lt;ContextToUse.Consumer&gt;
  6. {this.renderWrappedComponent}
  7. &lt;/ContextToUse.Consumer&gt;
  8. )
  9. }

Context.Consumer内部必须是一个函数,这个函数的参数就是Context.Providervalue,也就是reduxstore

renderWrappedComponent

最后一个函数:renderWrappedComponent


  1. renderWrappedComponent(value) {
  2. /* ...验证参数有效性... */
  3. // 这里 storeState=store.getState()
  4. const { storeState, store } = value
  5. // 传入自定义组件的props
  6. let wrapperProps = this.props
  7. let forwardedRef
  8. if (forwardRef) {
  9. wrapperProps = this.props.wrapperProps
  10. forwardedRef = this.props.forwardedRef
  11. }
  12. // 上面已经讲了,返回最终数据
  13. let derivedProps = this.selectDerivedProps(
  14. storeState,
  15. wrapperProps,
  16. store
  17. )
  18. // 返回最终渲染的自定义组件
  19. return this.selectChildElement(derivedProps, forwardedRef)
  20. }

总算结束了,可能有点混乱,做个总结吧。


总结

我把react-redux的执行流程分为3个阶段,分别对应我们的代码编写(搭配导图阅读)


一张导图:


第一阶段:

对应的用户代码:


  1. &lt;Provider store={store}&gt;
  2. &lt;App /&gt;
  3. &lt;/Provider&gt;

执行内容有:

  1. 定义了Provider组件,这个组件内部订阅了reduxstore,保证当store发生变动,会立刻执行更新。

第二阶段:

对应的用户代码:


  1. connect(mapStateToProps,mapDispatchToProps,mergeProps,options)

执行内容有:

  1. connect接收了参数(mapStateToProps,mapDispatchToProps,mergeProps,options)。
  2. connect接收了对参数如何处理方案(3个...Factories)。
  3. connect接收了参数的结果比较方案(selectFactory)
  4. 定义了比较方案(4个are...Equal,其实就是全等比较浅比较)。

第三阶段:

对应的用户代码:


  1. let newComponent=connect(...)(Component)
  2. &lt;newComponent /&gt;

执行内容有:

  1. 接受自定义组件(Component)。
  2. 创建一个Connect组件。
  3. Component的非React静态方法转移到Connect
  4. 获取Provider传入的数据(redux的整个数据),利用闭包保存数据,用于和未来数据做比较。
  5. 当比较(===)有变动,执行上一阶段传入的参数,获取当前组件真正的数据。
  6. 利用闭包保存当前组件真正的数据,用于和未来作比较。
  7. 通过全等和浅比较,处理state变动和props变动的逻辑,判断返回新数据还是旧数据。
  8. 利用闭包保存渲染的组件,通过上面返回的最终数据,判断需要返回新组件还是就组件。

逻辑理顺了,还是很好理解的。

其中第三阶段就是对外APIconnectAdvanced的执行内容。


此处查看更多前端源码阅读内容。

或许哪一天,我们需要设计一个专用的数据管理系统,那么就利用好connectAdvanced
我们要做的就是编写一个自定义第二阶段的逻辑体系。

感谢阅读!

来源:https://segmentfault.com/a/1190000017062631

高性能和可扩展的React-Redux的更多相关文章

  1. webpack+react+redux+es6开发模式

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  2. angular开发者吐槽react+redux的复杂:“一个demo证明你的开发效率低下”

    曾经看到一篇文章,写的是jquery开发者吐槽angular的复杂.作为一个angular开发者,我来吐槽一下react+redux的复杂. 例子 为了让大家看得舒服,我用最简单的一个demo来展示r ...

  3. webpack+react+redux+es6

    一.预备知识 node, npm, react, redux, es6, webpack 二.学习资源 ECMAScript 6入门 React和Redux的连接react-redux Redux 入 ...

  4. React+Redux开发实战项目【美团App】,没你想的那么难

    README.md 前言 开始学习React的时候,在网上找了一些文章,读了官网的一些文档,后来觉得React上手还是蛮简单的, 然后就在网上找了一个React实战的练手项目,个人学完之后觉得这个项目 ...

  5. React+Redux实现追书神器网页版

    引言 由于现在做的react-native项目没有使用到redux等框架,写了一段时间想深入学习react,有个想法想做个demo练手下,那时候其实还没想好要做哪一个类型的,也看了些动漫的,小说阅读, ...

  6. React Redux 与胖虎

    这是一篇详尽的 React Redux 扫盲文. 对 React Redux 已经比较熟悉的同学可以直接看 <React Redux 与胖虎他妈>. 是什么 React Redux 是 R ...

  7. react+redux教程(六)redux服务端渲染流程

    今天,我们要讲解的是react+redux服务端渲染.个人认为,react击败angular的真正“杀手锏”就是服务端渲染.我们为什么要实现服务端渲染,主要是为了SEO. 例子 例子仍然是官方的计数器 ...

  8. react+redux教程(五)异步、单一state树结构、componentWillReceiveProps

    今天,我们要讲解的是异步.单一state树结构.componentWillReceiveProps这三个知识点. 例子 这个例子是官方的例子,主要是从Reddit中请求新闻列表来显示,可以切换reac ...

  9. react+redux官方实例TODO从最简单的入门(6)-- 完结

    通过实现了增-->删-->改-->查,对react结合redux的机制差不多已经了解,那么把剩下的功能一起完成吧 全选 1.声明状态,这个是全选状态 2.action约定 3.red ...

  10. react+redux官方实例TODO从最简单的入门(1)-- 前言

    刚进公司的时候,一点react不会,有一个需求要改,重构页面!!!完全懵逼,一点不知道怎么办!然后就去官方文档,花了一周时间,就纯react实现了页面重构,总体来说,react还是比较简单的,由于当初 ...

随机推荐

  1. 对includes的研究

    1.includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false. 2.let site = ['runoob', 'google', 'taobao']; s ...

  2. ubuntu 14.04 安装openjdk 8

    最近准备在ubuntu14.04上安装Oracle,但是需要提前安装jdk,发现问题挺多的,后面看到了如下的操作步骤,成功安装,特意记录下来. 致谢:https://www.yangshenglian ...

  3. 2017年cocoaPods 1.2.1升级

    还在用老版本的ccoaPods,安装三方库时,会报错 : [!] Invalid `Podfile` file: [!] The specification of `link_with` in the ...

  4. STM32开发板的TIM3开启和关闭

    关闭定时器中断要考虑好多情况 1)关闭定时器时,定时器是否在处在工作状态 2)关闭定时器时,定时器是否正好进入中断,造成关闭程序出现断层,进而无法实现完整关闭程序,此时可以使用高一级别的外部中断强制进 ...

  5. 如何在ASP.NET Core中上传超大文件

    HTML部分 <%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="index.aspx. ...

  6. luogu P1068 分数线划定 x

    P1068 分数线划定 题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行.为了选拔最合适的人才,A 市对 所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试.面试分数线根 据 ...

  7. Shell的简单介绍(一)

    shell 的分类 Shell 类别 易学性 可移植性 编辑性 快捷性 Bourne Shell (sh) 容易 好 较差 较差 Korn Shell (ksh) 较难 较好 好 较好 Bourne ...

  8. python 指定画图分辨率

    from IPython.core.pylabtools import figsize # import figsize figsize(12.5, 4) # 设置 figsize plt.rcPar ...

  9. formdata方式上传文件,支持大文件分割上传

    1.upload.html <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/html"> <h ...

  10. jquery ajax实现文件上传

    test5.html <html> <head> <meta http-equiv="Content-Type" content="text ...