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的逻辑

  • 相关阅读:
    checkpoint threat cloud 更改中国区更新库
    2021关于算法的“想象”
    安装Hadoop
    BUG:@RabbitListener的concurrency属性
    Redisson使用01
    spring boot项目整合mybatis访问数据源
    MyBatis Generator使用记录
    spring boot集成Swagger
    PostgreSQL常用命令(持续更新)
    2021Y12M学技术感悟
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/11748776.html
Copyright © 2011-2022 走看看