React源码学习(2)------FilberNode 和 FilberRootNode

一、概述

在React内部,会将所有的React元素转换为Filber树 Filber树总体结构图示

在这张图里,出现了filberRootNode,其他的节点都是FilberNode类型,hostRootFilber就是最外层的FilberNode。

在项目里面,当我们调用ReactDOM.createRoot(root)方法的时候,就会创建出上面的FilberRootNode和hostRootFilber。并且将二者之间的指向关系确定。

二、FilberNode

FilberNode 是 React 中用于表示虚拟 DOM 树中节点的基本数据结构。它包含了丰富的信息:

(1)type

type:可以是字符串(如 'div'、'span' 等表示 HTML 标签类型)、函数(代表自定义组件)或者其他特定类型(如 React.Fragment),用于标识节点的类型。

(2)tag

在 FilberNode 结构里,tag属性主要用于标识节点的类型。这个类型可以是多种情况。

对于原生 HTML 元素,tag属性的值就是 HTML 标签名。例如,如果 FilberNode 代表一个<div>元素,那么tag的值就是"div";如果是一个<span>元素,tag就是"span"。这使得 React 在渲染阶段能够根据这个标签名创建对应的真实 DOM 元素。

对于自定义组件,tag属性存储的是组件的引用(可以是函数组件或者类组件的引用)。比如,有一个自定义函数组件MyComponent,在 FilberNode 表示这个组件时,tag属性的值就是MyComponent这个函数的引用。

js 复制代码
const FunctionComponent = 'FunctionComponent'; //对应函数
const HostRoot= 'HostRoot'; //对应hostRootFilber
const HostComponent= 'HostComponent'; //对应div
const HostText = 'HostText'; //对应文本节点

(3)key

key:在列表渲染等场景中用于优化渲染和复用节点。它是一个唯一标识符,帮助 React 确定在更新过程中节点是否发生了变化,对应的就是ReactElement中的key。

(4)ref

ref:用于获取对真实 DOM 节点的引用,方便开发者在特定情况下与 DOM 进行交互,对应的就是ReactElement中的ref。

(5)stateNode

stateNodestateNode属性主要用于存储与该虚拟节点对应的真实 DOM 节点。这意味着在虚拟 DOM 的世界里,每一个 FilberNode 都通过stateNode属性知道自己在真实浏览器环境中的 "物理存在"。例如,当一个 FilberNode 代表一个<div>标签时,stateNode就是浏览器内存中对应的真实<div>元素。

(6)alternate

alternatealternate属性用于存储与当前 FilberNode 对应的另一个版本的 FilberNode,这个另一个版本通常是在更新过程中创建的新节点。例如,当一个组件的状态发生变化,React 会构建一个新的 FilberNode 树来反映这个变化,旧的 FilberNode 和新的 FilberNode 通过alternate属性相互关联。

(7)pendingProps

在 FilberNode(React 虚拟 DOM 节点)的体系中,pendingProps是一个非常重要的属性。它存储了即将应用到该节点的属性(properties)。这些属性包含了各种信息,例如用于配置 HTML 元素的属性(如classstyleid等)或者传递给自定义组件的props

可以把pendingProps看作是一个 "待办事项" 列表,它记录了在下一次渲染阶段或者更新阶段需要应用到节点的所有属性相关的信息。

(8)memoizedProps

在 FilberNode 的结构中,memoizedProps用于存储上一次渲染时该节点所使用的属性(props)。它是 React 内部用于优化组件重新渲染的一个重要属性。

主要用于和pendingProps(即将应用到节点的属性)进行对比。通过这种对比,React 可以快速判断出属性是否发生了变化,从而决定是否需要重新渲染该节点及其子节点。这是一种性能优化策略,避免了不必要的 DOM 操作和组件重新渲染。

(9)memoizedState

在 React 组件的运行机制中,memoizedState是一个关键属性,尤其在类组件和部分基于 React 内部状态管理的函数组件场景下发挥重要作用。它主要用于存储组件已经记忆(memoized)的状态。

对于类组件而言,这个属性通常是在组件实例内部维护的,用来记录组件状态相关的信息,例如this.state的内容在内部可能会被存储在memoizedState中,以便在组件的生命周期内进行状态的管理和更新。

在函数组件中,当使用一些 React 的高级特性(如 Hooks)时,memoizedState也会参与到状态的保存和更新过程中。

(10)return

在 FilberNode的结构中,return属性用于指向该节点在更新过程中的 "返回" 节点。这个概念在 React 的协调(reconciliation)过程中比较重要,特别是在处理复杂的组件更新和复用场景时。

例如,当一个组件的一部分被更新,而这部分组件对应的 FilberNode 在更新后需要 "返回" 到某个父节点或者其他关联节点来完成更新流程,return属性就起到了这个指向作用。

在 React 的更新算法中,return属性帮助确定节点在更新后的挂载位置或者关联位置。假设一个 FilberNode 代表一个子组件,在更新过程中,如果这个子组件被移动到了一个新的位置或者被重新挂载,return属性可以确保它能够正确地与新的父节点或者周围的节点建立联系。这对于保持虚拟 DOM 树的结构完整性和更新的准确性非常重要。

(11)child

child属性指向该 FilberNode 的第一个子 FilberNode,它是构建虚拟 DOM 树的关键属性。通过这个属性,可以将多个 FilberNode 连接成一个树状结构,其中每个节点可以有自己的子节点,从而形成复杂的层次关系。

例如,对于一个代表<div>元素的 FilberNode,如果它包含一个<p>元素作为子元素,那么这个<div>元素对应的 FilberNode 的child属性就会指向代表<p>元素的 FilberNode。

(12)sibling

sibling属性用于指向同一层级的下一个 FilberNode(兄弟节点)。在虚拟 DOM 树的构建和遍历过程中,这个属性帮助建立起同一层级节点之间的顺序关系。

比如,在一个包含多个并列元素(如多个<li>元素在一个<ul>中)的组件中,每个代表<li>元素的 FilberNode 会通过sibling属性连接到下一个<li>元素对应的 FilberNode,从而形成一个链表结构来表示同一层级的节点顺序。

(13)index

index属性主要用于在节点列表(如数组渲染的组件列表)中标识节点的位置。它是一个数字,用于表示一个 FilberNode 在其兄弟节点列表中的索引位置。

例如,在一个通过map函数渲染的列表组件中,每个代表列表项的 FilberNode 会有一个index属性,用于区分不同的列表项,并且在更新过程中帮助 React 确定节点的相对位置。

在更新列表组件时,index属性对于确定节点是否被移动、添加或者删除非常重要。React 可以通过比较新旧节点的index属性来判断节点的位置变化情况。

同时,index属性在节点复用策略中也起到关键作用。例如,在shouldComponentUpdate或者React.memo的优化策略中,index属性可以作为一个参考因素,帮助判断节点是否可以复用,尤其是在处理列表组件的更新时。

三、FilberRootNode

FilberRootNode 是 React 应用中虚拟 DOM 树的根节点相关的数据结构。它在 React 的渲染和更新机制中扮演着至关重要的角色,是整个应用虚拟 DOM 架构的起始点。

(1)current

  • 含义current是 FilberRootNode 中一个关键的属性,它指向当前正在被渲染和使用的 FilberNode 树的根节点。这个 FilberNode 树代表了当前用户界面(UI)的状态。
  • 作用 :在 React 应用的运行过程中,当有更新发生时,React 会构建一个新的 FilberNode 树来反映更新后的状态。在合适的时机,current属性会被更新,从指向旧的 FilberNode 树切换到指向新的 FilberNode 树,从而实现 UI 的无缝更新。例如,当一个组件的状态改变导致整个 UI 需要重新渲染时,React 会创建新的 FilberNode 树,然后将 FilberRootNode 的current指向这个新树,完成 UI 从旧状态到新状态的过渡。

(2)containerInfo

  • 含义containerInfo属性包含了与挂载真实 DOM 相关的信息。它确定了整个 React 应用在真实 DOM 中的挂载位置,即告诉 React 应该将虚拟 DOM 挂载到哪个真实的 HTML 元素上。
  • 作用 :在 React 应用的初始挂载阶段,containerInfo发挥着关键作用。例如,通过ReactDOM.render方法将一个 React 应用挂载到一个idapp的 HTML 元素上时,containerInfo就会保存与这个idapp的元素相关的信息,使得 React 能够将虚拟 DOM 树转换为真实 DOM 并挂载到该元素内部,从而在浏览器中呈现出应用的界面。

(3)finishWork

在 React 的渲染和更新机制中,finishWork是一个关键的内部处理阶段相关概念。它主要涉及到对已经完成构建或者更新的 FilberNode(虚拟 DOM 节点)的最终处理工作。

这个阶段在整个渲染或更新流程中处于较后的位置,在 FilberNode 的创建、属性更新、子节点处理等操作基本完成之后,finishWork开始对这些节点进行整合、挂载真实 DOM(如果需要)或者其他收尾性质的操作。

四、定义ReactDOM

有了上面FilberNode和FilberRootNode的数据结构,我们可以实现FilberRootNode类和FilberNode类

js 复制代码
export class FilberRootNode {
  constructor(container, hostRootFiber) {
    this.container = container
    this.current = hostRootFiber
    hostRootFiber.stateNode = this
    this.finishedWork = null
  }
}
js 复制代码
export class FilberNode {
  constructor(tag, pendingProps, key) {
    this.tag = tag;
    this.key = key;
    this.stateNode = null;
    this.type = null;

    this.return = null;
    this.sibling = null;
    this.child = null;
    this.index = 0;

    this.ref = null;

    this.pendingProps = pendingProps;
    this.memoizedProps = null;
    this.memoizedState = null;
    this.alternate = null;
  }
}

在react项目中,我们是这样使用的ReactDOM的

js 复制代码
const root = document.getElementById('root')  
ReactDOM.createRoot(root).render()

所以,我们可以这样来实现ReactDOM

js 复制代码
function createRoot(root) {
  return {
    render() {

    }
  }
}

const ReactDOM = {
  createRoot
}

export default ReactDOM

五、实现createRoot方法

在调用createRoot方法后,我们会创建FilberRootNode对象,这里面我们封装成一个方法,

createContainer方法:

js 复制代码
function createContainer(root) {
  const hostRootFiber = new FilberNode(HostRoot, {}, '')
  return new FilberRootNode(root, hostRootFiber)
}

在createRoot方法中调用即可

js 复制代码
function createRoot(root) {
  const filberRootNode = createContainer(root)
  return {
    render() {

    }
  }
}
相关推荐
蟾宫曲1 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心1 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455661 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029401 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
魏时烟2 小时前
css文字折行以及双端对齐实现方式
前端·css
2401_882726483 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203983 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github
胡西风_foxww3 小时前
【ES6复习笔记】迭代器(10)
前端·笔记·迭代器·es6·iterator
前端没钱3 小时前
探索 ES6 基础:开启 JavaScript 新篇章
前端·javascript·es6
测试界萧萧4 小时前
15:00面试,15:08就出来了,问的问题有点变态。。。
自动化测试·软件测试·功能测试·程序人生·面试·职场和发展