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

这篇具有很好参考价值的文章主要介绍了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 element
        type: 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 树结构如下

React18原理: React核心对象之ReactElement对象和Fiber对象,React | React Native,react.js,前端,前端框架
  • 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 中文章来源地址https://www.toymoban.com/news/detail-826312.html

    // 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 output
      memoizedState: any,
    
      // Dependencies (contexts, events) for this fiber, if it has any
      dependencies: 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,
    
      // Effect
      flags: 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树
React18原理: React核心对象之ReactElement对象和Fiber对象,React | React Native,react.js,前端,前端框架
  • 这里的fiber树只是为了和上面的ReactElement树对照
  • 其中 <App/>, <Content/> 为ClassComponent类型的fiber节点
  • 其余节点都是普通 HostComponent 类型节点
  • <Content/> 的子节点在ReactElement树中是React.Fragment
  • 但是在 fiber 树中 React.Fragment并没有与之对应的fiber节点
  • reconciler阶段对此类型节点做了单独处理
  • 所以ReactElement节点和fiber节点不是一对一匹配

到了这里,关于React18原理: React核心对象之ReactElement对象和Fiber对象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【react】react18的学习(十二)– 底层原理(二)之 迭代器 iterator

    迭代器iterator 是一种 ES6 规范,具有这种机制的数据结构才可以使用 for of 循环:返回每一项的值; 原型链具有 Symbol.iterator 属性的数据结构都具备;如数组、部分类数组、字符串等; 普通对象就不能用; for-of循环原理:循环获取属性值; 执行可迭代原型链上的 Symbol.iterat

    2024年02月16日
    浏览(52)
  • 深入理解React中fiber

    Fiber是对React核心算法的重写,Fiber是React内部定义的一种数据结构,将更新渲染耗时长的大任务,分为许多的小片。Fiber节点保存啦组件需要更新的状态和副作用,一个Fiber代表一个工作单元。 在react中,主要做了下面这些操作: 为每个增加了优先级,优先级高的任务可以中断

    2024年02月07日
    浏览(35)
  • React Three Fiber动画

    使用静态对象和形状构建 3D 场景非常酷,但是当你可以使用动画使场景栩栩如生时,它会更酷。 在 3D 世界中,有一个称为角色装配的过程,它允许你创建称为骨架的特殊对象,其作用类似于骨骼和关节系统。 这些骨架连接到一块 3D 几何体上,例如人、动物或其他任何物体

    2024年02月02日
    浏览(45)
  • React核心原理与实际开发

    学习目标 React是啥? 官方定义:将前端请求获取到的 数据 渲染为 HTML视图 的 JavaScript库 。 直接创建react,使用初始化会创建package.json 再安装 使用纯JS创建ReactDOM(元素) React.createElement()方法使用不是很灵活,知道就好; ReactDOM.render()方法渲染react元素很重要!!!使用起来

    2024年02月07日
    浏览(41)
  • React 底层 Fiber 架构 简单理解

    JS 是引擎是 单线程运行 的;严格来说,JS 引擎和页面渲染引擎在同一渲染线程,两者 互斥 。那么就会遇到这样的一种情况:当前面一个任务长期霸占CPU,后面啥事也干不了,浏览器卡死,造成极差的用户体验。 对于前端而言,主要的解决方向: 优化每个任务,让它有多快

    2024年02月05日
    浏览(49)
  • React18原理: 生命周期中特别注意事项

    概述 生命周期就是一个组件从诞生到销毁的全过程(包含错误捕获,这里暂且不聊这个) react 在组件的生命周期中注册了一系列的钩子函数 支持开发者在其中嵌入代码,并在适当的时机运行 生命周期本质上就是组件中的钩子函数,主要有三个主要的钩子 挂载 更新 卸载 首次挂

    2024年02月20日
    浏览(35)
  • 手写react--fiber架构如何中断和恢复

    React 17 版本开始提供了一个新的JSX转换方案。在之前,JSX 代码会被默认编译成  React.createElement(...)  ,而使用新 JSX 方案后,会从一个独立的模块 react/jsx-runtime 中引入 JSX 函数。 babel插件@babel/plugin-transform-react-jsx /** @jsxRuntime classic */  babel运行时转换 render 创建最初始的fiber对

    2024年02月16日
    浏览(51)
  • Three.js 的组件库react-three-fiber和react-three-drei

    类似于这种字体,可以用Text或者Text3d,但是要处理一个问题,就是要保证字体一直正面视角。 这中间的处理比较的麻烦,于是可以使用react-three-drei中的Html来做这件事

    2024年01月24日
    浏览(63)
  • 使用 React Three Fiber 和 GSAP 实现 WebGL 轮播动画

    参考:Building a WebGL Carousel with React Three Fiber and GSAP 在线 demo github 源码 效果来源于由 Eum Ray 创建的网站 alcre.co.kr,具有迷人的视觉效果和交互性,具有可拖动或滚动的轮播,具有有趣的图像展示效果。 本文将使用 WebGL、React Three Fiber 和 GSAP 实现类似的效果。通过本文,可以了

    2024年02月05日
    浏览(60)
  • 【three.js / React-three-fiber】加载3D模型性能优化

    无论是大型虚拟世界还是简单的网站,性能优化都是必要的。 特别是在运用三维模型的情况下,我们需要更加深入的优化。因为 三维模型通常包含大量的数据和复杂的几何形状 ,如果不进行性能优化,浏览器可能会因为负载过重而崩溃。 在本文中,我们将探讨如何在 thre

    2024年02月02日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包