zoukankan      html  css  js  c++  java
  • 【React源码学习】Suspense && Children

    Suspense

    16.6 提供的一个feature,在线源码地址:https://github.com/facebook/react/blob/master/packages/react/src/React.js

    在一个Suspense组件,它下面渲染了一个或者多个异步的组件,有任何一个组件throw了一个promise之后,在这个promise的resolved之前,都会显示fallback中的内容。

    也就是说,在一个Suspense组件中,有多个组件,它要等它里面所有组件都resolved之后,才会撤销掉fallback中的内容

    demo

    // suspense/index.js
    import React, { Suspense, lazy } from 'react'
    
    const LazyComp = lazy(() => import('./lazy.js'))
    
    let data = ''
    let promise = ''
    function requestData() {
      if (data) return data
      if (promise) throw promise
      promise = new Promise(resolve => {
        setTimeout(() => {
          data = 'Data resolved'
          resolve()
        }, 2000)
      })
      throw promise
    }
    
    function SuspenseComp() {
      const data = requestData()
    
      return <p>{data}</p>
    }
    
    export default () => (
      <Suspense fallback="loading data">
        <SuspenseComp />
        <LazyComp />
      </Suspense>
    )
    
    // suspense/lazy.js
    
    import React from 'react'
    
    export default () => <p>Lazy Comp</p>
    
    
    

    源码

    // React.js
    
    ...
    Suspense: REACT_SUSPENSE_TYPE // 还是一个常量
    ...
    
    
    // https://github.com/facebook/react/blob/master/packages/react/src/ReactLazy.js
    import type {LazyComponent, Thenable} from 'shared/ReactLazyComponent';
    
    import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
    
    export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
      return {
        $$typeof: REACT_LAZY_TYPE,
        _ctor: ctor,  // 传进来的参数
        // React uses these fields to store the result.
        _status: -1, // 用来记录当前thenable这个对象处于的状态。当渲染到lazy component状态的时候,会调用这个ctor,然后返回一个thenable对象,一般来说是一个promise,处于pending状态,-1
        _result: null, // 用来记录Thenable resolved之后返回的属性,在lazy最终resolved出来的组件,放在里面。后续我们渲染lazy component的时候,直接拿result
      };
    }
    
    

    Children

    先看一个demo

    import React from 'react'
    
    function ChildrenDemo(props) {
      console.log(props.children)
      // 如果React.Children.map 返回的是一个数组,它会继续把它们展开成一维数组 [1, 1, 1, 2, 2, 2]
      console.log(React.Children.map(props.children, c => [c, [c, c]]))
      return props.children
    }
    
    export default () => (
      <ChildrenDemo>
        <span>1</span>
        <span>2</span>
      </ChildrenDemo>
    )
    
    

    在线源码地址

    https://github.com/facebook/react/blob/master/packages/react/src/ReactChildren.js(点击进入)

    图解源码流程

    mapIntoWithKeyPrefixInternal

    【它是一个大递归】,在此做了以下几件事情:

    1. 处理key
    2. 在此做一个非常重要的事情,就是去context pool中去获取一个context
    3. traverseAllChildren
    4. 后续所有流程完,会把这个context归还
    

    traverseAllChildren

    在此做了以下几件事情:

    1. children为null的情况return 0
    2. traverseAllChildrenImpl
    

    traverseAllChildrenImpl

    【它是一个小递归】,根据react element的type做了以下几件事情:

    1. 对应符合规则中的几种type,调用回调,即mapSingleChildIntoContext;
    2. 如果是数组,递归自身traverseAllChildrenImpl
    3. 如果是function,递归自身traverseAllChildrenImpl
    4. 如果是obejct, 进行相应处理

    mapSingleChildIntoContext

    做了以下几个事情

    1. 从传入进来的参数(pool context)中拿出func
    2. 调用func,并传入节点
    3. 判断func return的结果是否为数组,如果是,大递归mapIntoWithKeyPrefixInternal。(注意:大递归mapIntoWithKeyPrefixInternal传入的最后一个参数是c ⇒ c,直接返回这个节点。因为如果再次调用我们传入的方法,就会变成一个无限返回数组的递归)
    4. 如果3不成立,且返回的结果不为null,则表示当前已经遍历到了数据数组根节点。判断结果是否合理,通过cloneAndReplaceKey返回了一个新的react element,新的key,其他都相同,并在最终要map返回出去的数组中push了当前新的react element,到此结束

    小结:源码中运用了递归+资源池这样一个概念去编写相应逻辑。在此谈下对象池,当一个方法可能是一个经常要被调用的方法。如果该方法展开的比较多,声明和释放的对象多,这是一个非常需要性能的问题,尤其是在浏览器计算的过程中,可能需要去重复的去new 和delete一块内存空间,避免内存计算造成的性能抖动,从而去节省一部分的开销

    memo

    16.6 feature 目的是给function component也提供一个类似pure component的功能

    源码

    ...
    export default function memo<Props>(
      type: React$ElementType,
      compare?: (oldProps: Props, newProps: Props) => boolean,
    ) {
      ...
      return {
        $$typeof: REACT_MEMO_TYPE,
        type,
        compare: compare === undefined ? null : compare,
      };
    }
    
    

    从上面来看,最终返回了一个$$typeof为REACT_MEMO_TYPE的对象,真正的处理逻辑还需要在react-dom中学习

    fragment

    const functionComponent = () => {
        return <> 
            <span>1</span>
        </>
    }
    
    

    <> 其实是React.Fragment

    源码

    Fragment: REACT_FRAGMENT_TYPE // 依然是一个常量,实际处理逻辑需要在react-dom中查看对此类型的处理
    
    

    StrictMode

    提供一些过时api的提醒

    源码

    StrictMode: REACT_STRICT_MODE_TYPE // 依然是个symbol
    
    

    cloneElement

    在ReactElement.js源码中,实现了对element进行了clone的逻辑

  • 相关阅读:
    poj 1088 滑雪
    位运算与bitset
    hdu 4607 Park Visit
    树的直径
    codeforces 495D Sonya and Matrix
    German Collegiate Programming Contest 2015(第三场)
    BAPC 2014 Preliminary(第一场)
    Benelux Algorithm Programming Contest 2014 Final(第二场)
    E. Reachability from the Capital(tarjan+dfs)
    poj2104 K-th Number(划分树)
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/11748776.html
Copyright © 2011-2022 走看看