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

原文地址:https://www.cnblogs.com/fe-linjin/p/11748776.html

时间: 2024-10-09 08:30:16

【React源码学习】Suspense && Children的相关文章

React躬行记(16)——React源码分析

React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版本很接近. 一.目录结构 React采用了由Lerna维护monorepo方式进行代码管理,即用一个仓库管理多个模块(module)或包(package).在React仓库的根目录中,包含三个目录: (1)fixtures,给源码贡献者准备的测试用例. (2)packages,React库提供的包的

React源码 React.Children

children是什么意思呢?就是我们拿到组件内部的props的时候,有props.children这么一个属性,大部分情况下,我们直接把 props.children 渲染到 JSX 里面就可以了.很少有情况我们需要去操作这个 children . 但是如果一旦你需要去操作这个 children .我们推荐使用 React.children 的 api , 而不是直接去操作他. 虽然说我们大部分情况下拿到的 children 是合理的 react element 或者是一个数组,但是 Reac

【iScroll源码学习01】准备阶段 - 叶小钗

[iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文  http://www.cnblogs.com/yexiaochai/p/3496369.html 主题 iScroll HTML JavaScript ① viewport相关知识点(device-width等) ②  CSS3硬件加速 ③ 如何暂停CSS动画 ④ e.preventDefault导致文本不能获取焦点解决方案 ...... 当然,我们写的demo自然不能和

jQuery源码学习感想

还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码,那时我不明白他们为何要求那么高,现在才知道,原来没那么高,他问的都是jQuery最基本的框架架构,不过对于不知道的来说,再简单我也是不知道,那时写了一篇博文去吐槽了一下,那时候也是我自己真正激发自己的时候,那时候我说我一定要搞好自己的jQuery基础,没想到那么快就实现了,一个月的源码学习时间就结束

Spring源码学习笔记(5)

Spring源码学习笔记(五) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门 写下一句话,开篇不尴尬  ----  上篇文章中梳理到 Spring 加载资源文件后开始解析 Bean, 现在我们从两个解析函数 parseDefaultElement() 和 par

Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.View; public class UnderstandDispatchTouchEvent { /** * dispatchTouchEvent()源码学习及其注释 * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchE

jQuery源码学习笔记五 六 七 八 转

jQuery源码学习笔记五 六 七 八 转 Js代码   <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等.接着是其缓存机制data.</p> <pre class="brush:javascript;gutter:false;toolbar:false"> //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/ru

【iScroll源码学习03】iScroll事件机制与滚动条的实现

[iScroll源码学习03]iScroll事件机制与滚动条的实现 前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01]准备阶段 3. [iScroll源码学习00]模拟iScroll 今天我们来学习其事件机制以及滚动条的实现,完了后我们iScroll就学习的差不多了,最后会抽离iScroll的精华部分组成一个阉割版iScroll 并总结下iScr

jQuery 源码学习 - 01 - 简洁的 $(&#39;...&#39;)

首先贴上学习参考资料:[深入浅出jQuery]源码浅析--整体架构,备用地址:chokcoco/jQuery-. jQuery 库,js 开发的一个里程碑,它的出现,让网页开发者们告别荒蛮的上古时代,初步解放生产力,正式进入黄铜时代. 虽然如今 Angular/React/Vue 三驾马车驰骋畋猎,jQuery的时代渐行渐远,但是它的思想它的设计都有着里程碑式的作用.当然,我是拿它来补基础的,逃... 1.自执行函数 (function(params) { // ... })(Variable)