React18原理: React核心对象之ReactElement对象和Fiber对象

React中的核心对象

  • 在React应用中,有很多特定的对象或数据结构.了解这些内部的设计,可以更容易理解react运行原理
  • 列举从react启动到渲染过程出现频率较高,影响范围较大的对象,它们贯穿整个react运行时
    • 如 ReactElement 对象
    • 如 Fiber 对象
  • 其他过程的重要对象
    • 如事件对象(位于react-dom/events保障react应用能够响应ui交互)
    • 如 ReactContext, ReactProvider, ReactConsumer对象等

ReactElement对象

  • 入口函数 ReactDOM.render(<App />, document.getElementByld('root');

  • 可以简单的认为,包括 <App/> 及其所有子节点都是ReactElement对象(在render之后才会生成子节点)

  • 每个ReactElement对象的区别在于type不同,其type定义在shared包中

  • 所有采用jsx语法书写的节点,都会被编译器转换,最终会以React.createElement(..)的方式

  • 创建出来一个与之对应的ReactElement对象

  • ReactElement对象的数据结构如下,定义在: packages/shared/ReactElementType.js

    /*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/export type ReactElement = {$$typeof: any,type: any,key: any,ref: any,props: any,// ReactFiber_owner: any,// __DEV___store: {validated: boolean, ...},
    };
    
  • 特别关注 key 和 type 这两个属性

    • key 属性在reconciler阶段会用到
      • 所有的ReactElement对象都有key属性
      • 且其默认值是null
      • 后续在diff算法中会使用到
    • type 属性决定了节点的种类
      • 它的值可以是字符串(代表div,span等dom节点),函数(代表function,class等节点)
      • 或者react内部定义的节点类型(portal,context,fragment等)
      • 在reconciler阶段,会根据type执行不同的逻辑
        • 如type是一个字符串类型,则直接使用.
        • 如type是一个ReactComponent类型,则会调用其render方法获取节点.
        • 如type是一个function类型,则会调用该方法获取子节点
  • ReactElement 的工厂方法如下,定义在:packages/react/src/ReactElementProd.js

    /*** Factory method to create a new React element. This no longer adheres to* the class pattern, so do not use new to call it. Also, instanceof check* will not work. Instead test $$typeof field against Symbol.for('react.element') to check* if something is a React Element.** @param {*} type* @param {*} props* @param {*} key* @param {string|object} ref* @param {*} owner* @param {*} self A *temporary* helper to detect places where `this` is* different from the `owner` when React.createElement is called, so that we* can warn. We want to get rid of owner and replace string `ref`s with arrow* functions, and as long as `this` and owner are the same, there will be no* change in behavior.* @param {*} source An annotation object (added by a transpiler or otherwise)* indicating filename, line number, and/or other information.* @internal*/
    function ReactElement(type, key, ref, owner, props) {const element = {// This tag allows us to uniquely identify this as a React Element$$typeof: REACT_ELEMENT_TYPE,// Built-in properties that belong on the elementtype: type,key: key,ref: ref,props: props,// Record the component responsible for creating this element._owner: owner,};if (__DEV__) {// The validation flag is currently mutative. We put it on// an external backing store so that we can freeze the whole object.// This can be replaced with a WeakMap once they are implemented in// commonly used development environments.element._store = {};// To make comparing ReactElements easier for testing purposes, we make// the validation flag non-enumerable (where possible, which should// include every environment we run tests in), so the test framework// ignores it.Object.defineProperty(element._store, 'validated', {configurable: false,enumerable: false,writable: true,value: false,});// debugInfo contains Server Component debug information.Object.defineProperty(element, '_debugInfo', {configurable: false,enumerable: false,writable: true,value: null,});if (Object.freeze) {Object.freeze(element.props);Object.freeze(element);}}return element;
    }
    
  • 举一个App组件的例子

    class App extends React.Component {render() {return (<div className="app"><header>header</header><Content /><footer>footer</footer></div>)}
    }class Content extends React.Component {render() {return (<React.Fragment><p>1</p><p>2</p><p>3</p></React.Fragment>);}
    }export default App;
    
  • 这个代码,对应着 ReactElement 树结构如下

  • class和function类型的组件,其子节点是在render之后(reconciler阶段)才生成的
  • 此处只是单独表示 ReactElement 的数据结构
  • 父级对象和子级对象之间是通过props.children属性进行关联的(与fiber树不同)
  • ReactElement虽然不能算是一个严格的树,也不能算是一个严格的链表
    • 它的生成过程是自顶向下的,是所有组件节点的总和
    • ReactElement树(暂且用树来表述)和fiber树是以props.children为单位先后交替生成的
    • 当ReactElement树构造完毕,fiber树也随后构造完毕
  • reconciler阶段会根据ReactElement的类型生成对应的fiber节点
    • 注意,不是一一对应
    • 比如,Fragment类型的组件在生成fiber节点的时候会略过

Fiber对象

  • react-reconciler包是react应用的中枢

  • 连接渲染器(react-dom)和调度中心(scheduler),同时自身也负责fiber树的构造

  • 先看数据结构,其type类型的定义在 ReactInternalTypes.js 中

    // A Fiber is work on a Component that needs to be done or was done. There can
    // be more than one per component.
    export type Fiber = {// These first fields are conceptually members of an Instance. This used to// be split into a separate type and intersected with the other Fiber fields,// but until Flow fixes its intersection bugs, we've merged them into a// single type.// An Instance is shared between all versions of a component. We can easily// break this out into a separate object to avoid copying so much to the// alternate versions of the tree. We put this on a single object for now to// minimize the number of objects created during the initial render.// Tag identifying the type of fiber.tag: WorkTag,// Unique identifier of this child.key: null | string,// The value of element.type which is used to preserve the identity during// reconciliation of this child.elementType: any,// The resolved function/class/ associated with this fiber.type: any,// The local state associated with this fiber.stateNode: any,// Conceptual aliases// parent : Instance -> return The parent happens to be the same as the// return fiber since we've merged the fiber and instance.// Remaining fields belong to Fiber// The Fiber to return to after finishing processing this one.// This is effectively the parent, but there can be multiple parents (two)// so this is only the parent of the thing we're currently processing.// It is conceptually the same as the return address of a stack frame.return: Fiber | null,// Singly Linked List Tree Structure.child: Fiber | null,sibling: Fiber | null,index: number,// The ref last used to attach this node.// I'll avoid adding an owner field for prod and model that as functions.ref:| null| (((handle: mixed) => void) & {_stringRef: ?string, ...})| RefObject,refCleanup: null | (() => void),// Input is the data coming into process this fiber. Arguments. Props.pendingProps: any, // This type will be more specific once we overload the tag.memoizedProps: any, // The props used to create the output.// A queue of state updates and callbacks.updateQueue: mixed,// The state used to create the outputmemoizedState: any,// Dependencies (contexts, events) for this fiber, if it has anydependencies: Dependencies | null,// Bitfield that describes properties about the fiber and its subtree. E.g.// the ConcurrentMode flag indicates whether the subtree should be async-by-// default. When a fiber is created, it inherits the mode of its// parent. Additional flags can be set at creation time, but after that the// value should remain unchanged throughout the fiber's lifetime, particularly// before its child fibers are created.mode: TypeOfMode,// Effectflags: Flags,subtreeFlags: Flags,deletions: Array<Fiber> | null,// Singly linked list fast path to the next fiber with side-effects.nextEffect: Fiber | null,// The first and last fiber with side-effect within this subtree. This allows// us to reuse a slice of the linked list when we reuse the work done within// this fiber.firstEffect: Fiber | null,lastEffect: Fiber | null,lanes: Lanes,childLanes: Lanes,// This is a pooled version of a Fiber. Every fiber that gets updated will// eventually have a pair. There are cases when we can clean up pairs to save// memory if we need to.alternate: Fiber | null,// Time spent rendering this Fiber and its descendants for the current update.// This tells us how well the tree makes use of sCU for memoization.// It is reset to 0 each time we render and only updated when we don't bailout.// This field is only set when the enableProfilerTimer flag is enabled.actualDuration?: number,// If the Fiber is currently active in the "render" phase,// This marks the time at which the work began.// This field is only set when the enableProfilerTimer flag is enabled.actualStartTime?: number,// Duration of the most recent render time for this Fiber.// This value is not updated when we bailout for memoization purposes.// This field is only set when the enableProfilerTimer flag is enabled.selfBaseDuration?: number,// Sum of base times for all descendants of this Fiber.// This value bubbles up during the "complete" phase.// This field is only set when the enableProfilerTimer flag is enabled.treeBaseDuration?: number,// Conceptual aliases// workInProgress : Fiber ->  alternate The alternate used for reuse happens// to be the same as work in progress.// __DEV__ only_debugInfo?: ReactDebugInfo | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,_debugNeedsRemount?: boolean,// Used to verify that the order of hooks does not change between renders._debugHookTypes?: Array<HookType> | null,
    };
    
    • fiber.tag
      • 表示fiber类型,根据 ReactElement 组件的type进行生成,在react内部共定义了25种tag
    • fiber.key
      • ReactElement 组件的key一致
    • fiber.elementType
      • 一般来讲和ReactElement组件的type一致
    • fiber.type
      • 一般来讲和fiber.elementType一致.一些特殊情形下,比如在开发环境下为了兼容热更新 (HotReloading)
      • 会对 function, class, ForwardRef类型的 ReactElement做一定的处理
      • 这种情况会区别于fiber.elementType
    • fiber.stateNode
      • 与fiber关联的局部状态节点
      • 比如:HostComponent 类型指向与fiber节点对应的dom节点
      • 根节点 fiber.stateNode 指向的是 FiberRoot
      • class 类型节点其 stateNode 指向的是 class 实例
    • fiber.return
      • 指向父节点
    • fiber.child
      • 指向第一个子节点
    • fiber.sibling
      • 指向下一个兄弟节点
    • fiber.index
      • fiber在兄弟节点中的索引,如果是单节点默认为0.
    • fiber.ref
      • 指向在ReactElement组件上设置的ref
      • string类型的ref除外,这种类型的ref已经不推荐使用
      • reconciler阶段会将string类型的ref转换成一个function类型
    • fiber.pendingProps
      • 输入属性,从 ReactElement对象传的props
      • 用于和 fiber.memoizedProps 比较可以得出属性是否变动
    • fiber.memoizedProps
      • 上一次生成子节点时用到的属性,生成子节点之后保持在内存中
      • 向下生成子节点之前叫做pendingProps
      • 生成子节点之后会把pendingProps赋值给 memoizedProps用于下一次比较
      • pendingProps和memoizedProps比较可以出属性是否变动
    • fiber.updateQueue
      • 存储update更新对象的队列,每一次发起更新,都需要在该队列上创建一个update对象
    • fiber.memoizedState
      • 上一次生成子节点之后保持在内存中的局部状态
    • fiber.dependencies
      • 该fiber节点所依赖的(contexts, events)等
    • fiber.mode
      • 二进制位 Bitfield, 继承至父节点,影响本fiber节点及其子树中所有节点
      • 与react应用的运行模式有关(有ConcurrentMode, NoMode等选项)
    • fiber.flags
      • 标志位,副作用标记(在16.x版本中叫做effectTag)
      • 在 ReactFiberFlags.js中定义了所有的标志位
      • reconciler阶段会将所有拥有flags标记的节点添加到副作用链表中,等待commit阶段的处理
    • fiber.subtreeFlags
      • subtreeFlags是一个二进制形式的属性,它代表了Fiber节点子树的操作依据
      • 换句话说,它包含了有关该节点及其所有子节点的更新信息。这是React内部用于优化和协调更新的重要机制之一
      • 具体来说,subtreeFlags可能包含各种标志,这些标志指示了哪些子节点需要更新、哪些操作正在进行中、哪些操作已经完成等
      • React使用这些标志来有效地管理组件的更新过程,确保只更新实际发生变化的部分,从而提高性能和效率
      • 需要注意的是,subtreeFlags是React内部使用的属性,对于大多数开发者来说,通常不需要直接与之交互
      • React的公开API和抽象层使得开发者可以专注于编写组件逻辑,而无需关心底层的更新和渲染机制
    • fiber.deletions
      • 存储将要被删除的子节点.默认未开启,
    • fiber.nextEffect
      • 单向链表,指向下一个有副作用的fiber节点
    • fiber.firstEffect
      • 指向副作用链表中的第一个fiber节点
    • fiber.lastEffect
      • 指向副作用链表中的最后一个fiber节点.
    • fiber.lanes
      • 本fiber节点所属的优先级,创建fiber的时候设置
    • fiber.childLanes
      • 子节点所属的优先级
    • fiber.alternate
      • 指向内存中的另一个fiber,每个被更新过fiber节点在内存中都是成对出现(current 和 workInProgress)

  • 绘制与ReactElement对应的一棵Fiber树
  • 这里的fiber树只是为了和上面的ReactElement树对照
  • 其中 <App/>, <Content/> 为ClassComponent类型的fiber节点
  • 其余节点都是普通 HostComponent 类型节点
  • <Content/> 的子节点在ReactElement树中是React.Fragment
  • 但是在 fiber 树中 React.Fragment并没有与之对应的fiber节点
  • reconciler阶段对此类型节点做了单独处理
  • 所以ReactElement节点和fiber节点不是一对一匹配

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/694174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IO 作业 24/2/21

1、使用多线程完成两个文件的拷贝&#xff0c;第一个线程拷贝前一半&#xff0c;第二个线程拷贝后一半&#xff0c;主线程回收两个线程的资源 #include <myhead.h> //定义分支线程1 void *task1(void *arg) {int fdr-1;//只读打开被复制文件if((fdropen("./111.txt…

2024光伏展

2024年光伏展是一个专业的光伏行业展览会&#xff0c;旨在展示最新的光伏技术和产品&#xff0c;并促进光伏行业的发展和合作。 该展览会预计将吸引来自全球各地的光伏制造商、供应商、投资者和专业人士。参展的公司将有机会展示他们的最新产品和技术&#xff0c;与其他行业领导…

HTTP协议要点总结

一、什么是 HTTP 协议 1. 超文本传输协议 (HTTP &#xff0c; HyperText Transfer Protocol) 是互联网上应用广泛的一种网络协议。 是工作在 tcp/ip 协议基础上的 , 所有的 WWW 文件都遵守这个标准。 2. http1.0 短连接 http1.1 长连接 3. http 是 TCP/IP 协议的一个…

react实现转盘抽奖功能

看这个文章不错&#xff0c;借鉴 这个博主 的内容 样式是背景图片直接&#xff0c;没有设置。需要的话应该是 #bg { width: 650px; height: 600px; margin: 0 auto; background: url(turntable-bg.jpg) no-repeat; position: relative; } img[src^"pointer"] {positi…

马斯克称首位受试者可凭思维操控鼠标;字节低调推出视频模型丨 RTE 开发者日报 Vol.148

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

微信小程序uniapp校园在线报修系统维修系统java+python+nodejs+php

管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行删除&#xff0c;查询&#xff0c;添加&#xff0c;修改 4.维修工管理&#xff1a;对维修工信息进行添加&#xff0c;修…

SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比

场景 若依前后端分离版本地搭建开发环境并运行项目的教程&#xff1a; 若依前后端分离版手把手教你本地搭建环境并运行项目_本地运行若依前后端分离-CSDN博客 若依前后端分离版如何集成的mybatis以及修改集成mybatisplus实现Mybatis增强&#xff1a; https://blog.csdn.net…

28/100二叉树的中序遍历 29/100不同的二叉搜索树(TODO) 30/100验证二叉搜索树 31/100对称二叉树

题目&#xff1a;28/100 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() …

gitlab 项目上线,项目上线后回滚

gitlab 项目上线&#xff0c;项目上线后回滚 1.需要自己有个gitlab项目环境&#xff0c;没有找我&#xff0c;docker-compose 一键环境启动 2.发起合并请求3.选择合并的分支4.点击创建合并&#xff0c;然后确认合并合并完成&#xff0c;进行回滚操作&#xff0c;在合并详情页…

Vue | (三)使用Vue脚手架(下)| 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue 中的自定义事件&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;全局事件总线&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;消息订阅与发布&#x1f407;使用方法…

压缩感知常用的测量矩阵

测量矩阵的基本概念 在压缩感知&#xff08;Compressed Sensing&#xff0c;CS&#xff09;理论中&#xff0c;测量矩阵&#xff08;也称为采样矩阵&#xff09;是实现信号压缩采样的关键工具。它是一个通常为非方阵的矩阵&#xff0c;用于将信号从高维空间映射到低维空间&…

10、内网安全-横向移动域控提权NetLogonADCSPACKDC永恒之蓝

用途&#xff1a;个人学习笔记&#xff0c;有所借鉴&#xff0c;欢迎指正&#xff01; 背景&#xff1a; 主要针对内网主机中的 域控提权漏洞&#xff0c;包含漏洞探针和漏洞复现利用。 1、横向移动-系统漏洞-CVE-2017-0146&#xff08;ms17-010&#xff0c;永恒之蓝&#xff0…

彩虹工具网程序开源未加密版源码,支持插件扩展

全新UI彩虹站长在线工具箱系统源码下载 全开源版本 支持暗黑模式 支持高达72种站长工具、开发工具、娱乐工具等功能。本地调用API、自带免费API接口&#xff0c; 是一个多功能性工具程序支持后台管理、上传插件、添加增减删功能 下载地址你别走吖 Σ(っ Д ;)っ (chaobiji.c…

Android13 编译ninja failed with: exit status 137

描述 现象很奇怪&#xff0c;主机是ubuntu 18.04&#xff0c; 内存有32G&#xff0c;并且系统中有两份Android13代码&#xff0c; 有一份编译正常&#xff0c;另外一份编译不正常&#xff0c;一度以为是因为下载源码不齐全导致&#xff0c;后面仔细看日志&#xff0c;原来是内…

Windows Server 2012 安装

1.镜像安装 镜像安装:Windows Server 2012 2.安装过程(直接以图的形式呈现) 2012激活秘钥:J7TJK-NQPGQ-Q7VRH-G3B93-2WCQD

WatiN——Web自动化测试(一)

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Linux:gitlab创建组,创建用户,创建项目

创建组和项目 让后可以在组里创建一个个仓库 创建成员 我创建个成员再把他分配进这个组里 进入管理员 密码等会我们创建完用户再去配置密码 Regular是普通的用户&#xff0c;只可以正常去访问指定规则的项目 而下面的administrator就是管理员&#xff0c;可以随便进项目&…

基于springboot+vue的B2B平台的购物推荐网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

基于Web的网文管理系统

项目&#xff1a;基于Web的网文管理系统 伴随着时代的更替&#xff0c;我国人民的生活水平在不断提高&#xff0c;同时对生活娱乐的需求也在不断加大&#xff0c;我们在日常生活中通常会以多种方式娱乐自己&#xff0c;而阅读网络小说就是其中一种方式。随着计算机技术的成熟&a…

外包实在是太坑了,划水三年,感觉人都废了

先说一下自己的情况&#xff0c;专科生&#xff0c;19年通过校招进入杭州某个外包软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了3年的功…