React进阶之React核心源码解析(一)

news2025/2/23 4:20:47

React核心源码解析

  • react 特点
    • CPU卡顿
    • IO 卡顿
  • 新老 react 架构对比
    • v15
    • v16.8
      • Scheduler 调度器
      • Reconciler 协调器
  • React fiber
    • 原理
    • 更新dom
    • mount 构建过程
  • render阶段 — scheduler reconciler
  • react源码解析
    • react-dom
      • react-dom/src/client/ReactDOMRoot.js
    • react-reconciler
      • react-reconciler/src/ReactFiberReconciler.js
      • react-reconciler/src/ReactFiberWorkLoop.js
      • react-reconciler/src/ReactFiberBeginWork.js
      • react-reconciler/src/ReactChildFiber.js
      • react-reconciler/src/ReactFiber.js
      • react-reconciler/src/ReactFiberWorkLoop.js
  • commit阶段 同步阶段

cline + deeseek => AI工具,上手成本非常低,非常便宜
cursor 编辑器

带着问题学源码

  1. 为什么 react 会引入 fiber 架构
  2. 简述 fiber 节点的结构和作用
  3. fiber 架构 => 架构+流程
  4. diff 算法
  5. hooks 原理

学习方法论:由大到小 对比回答问题

react 特点

  1. 单向数据流
    能够快速响应用户操作
    公式:ui = render(data)

什么原因导致响应慢?
cpu卡顿,js执行导致画面卡顿
IO卡顿,网络问题等 延迟

CPU卡顿

浏览器一秒60hz,16.6ms刷新一次,超过就会有掉帧现象
就比如这个例子

// index.js
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");

// ReactDOM.render(<App />, rootElement);
ReactDOM.createRoot(rootElement).render(<App />);

// APP.js
import "./styles.css";

export default function App() {
  const len = 3000;

  return (
    <ul>
      {Array(len)
        .fill(0)
        .map((_, i) => (
          <li>{i}</li>
        ))}
    </ul>
  );
}

React是怎么解决的?
时间分片:把更新过程碎片化 优先级 应用更新 后台预渲染

  • 同步 阻塞 渲染
  • 异步 非阻塞 用户优先级渲染

16.8后提出的 concurrent mode

ReactDOM.createRoot(rootEl).render(<App />)

IO 卡顿

接口响应时间长,解决:

  • loading
  • suspence 兜底 fallback
  • error-boundary 错误兜底
    在这里插入图片描述

新老 react 架构对比

v15 同步的,不可中断的
v16.8 异步,可中断
v18 ssr升级 流式 ssr stream,针对页面所有组件是分块,分局部输出,而不是等所有页面拼接组装好后一次性传输给客户端
页面上的不同接口,包裹成流式,然后传输给客户端,提升TTFB(首字节响应时间)
性能优化有需求,使用nextjs做提升

v15

  • Reconciler 协调器,diff 负责找出变化的组件
    • update 更新
    • component render jsx 渲染 => vdom
    • vdom diff
    • 找出变化的元素
    • 通知 renderer 渲染

通过递归的方式找出变化的组件
mount 阶段 => 调用 mountComponent
update 阶段 => 调用 updateComponent

递归更新子组件
=> 缺点:层级深,递归时间超过16ms

  • Renderer 渲染器,负责将变化的组件渲染到页面上
    • ReactDom.render
    • ReactNative.render

不可中断,中断则后续内容不执行

v16.8

多了一个 Scheduler 调度器

  • Scheduler 调度器 调度任务的优先级
  • Reconciler 协调器 负责找出变化的组件 递归=>可中断
  • Renderer 渲染器 是拿着Reconciler提供的标识 同步渲染

Scheduler 调度器

将大型任务分割成小任务,每一帧分配一定的时间执行小任务

  • 时间切片
  • 优先级调度
  1. 每个工作单元,对应一个fiber节点
  2. 时间分配
  3. 调度循环 维护任务队列
  4. 时间检查
  5. 暂停与恢复
  6. 利用浏览器API
  • requestAnimationFrame 用于在下一帧开始的时候执行回调函数
  • requestIdleCallBack 浏览器空闲的时候执行回调函数 setTimeout 模拟了

Reconciler 协调器

// 更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。

/** @noinline */
function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while (workInProgress !== null && !shouldYield()) {
    workInProgress = performUnitOfWork(workInProgress);
  }
}

React fiber

原理

react 内部实现的数据结构,支持状态更新,可中断可恢复,恢复后可以复用之前的中间状态

  • 架构:v15 stack reconciler,v16 fiber reconciler
  • 数据结构:每个 fiber 节点对应 react element,多个组件类型,dom节点各种属性数据
  • 动态的工作单元,改变的状态,要执行的工作
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // Instance,静态节点的数据结构属性
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber,用来链接其他fiber节点形成的fiber树
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  // 作为动态的工作单元的属性
  this.pendingProps = pendingProps; //即将应用到组件上新属性 相当于是新值newValue 
  this.memoizedProps = null; // 上次渲染时使用的属性值 相当于是旧值oldValue
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // 记录变化的节点 effectlist=>render
  this.effectTag = NoEffect;
  this.subtreeTag = NoSubtreeEffect;
  this.deletions = null;
  this.nextEffect = null;

  // effectslist链 = firstEffect -> nextEffect -> nextEffect -> lastEffect => 交给render => 渲染
  this.firstEffect = null;
  this.lastEffect = null;

  // 作为调度优先级的属性
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // 指向该fiber在另一次更新时对应的fiber
  this.alternate = null;

  if (enableProfilerTimer) {
    // Note: The following is done to avoid a v8 performance cliff.
    //
    // Initializing the fields below to smis and later updating them with
    // double values will cause Fibers to end up having separate shapes.
    // This behavior/bug has something to do with Object.preventExtension().
    // Fortunately this only impacts DEV builds.
    // Unfortunately it makes React unusably slow for some applications.
    // To work around this, initialize the fields below with doubles.
    //
    // Learn more about this here:
    // https://github.com/facebook/react/issues/14365
    // https://bugs.chromium.org/p/v8/issues/detail?id=8538
    this.actualDuration = Number.NaN;
    this.actualStartTime = Number.NaN;
    this.selfBaseDuration = Number.NaN;
    this.treeBaseDuration = Number.NaN;

    // It's okay to replace the initial doubles with smis after initialization.
    // This won't trigger the performance cliff mentioned above,
    // and it simplifies other profiler code (including DevTools).
    this.actualDuration = 0;
    this.actualStartTime = -1;
    this.selfBaseDuration = 0;
    this.treeBaseDuration = 0;
  }

  if (__DEV__) {
    // This isn't directly used but is handy for debugging internals:
    this._debugID = debugCounter++;
    this._debugSource = null;
    this._debugOwner = null;
    this._debugNeedsRemount = false;
    this._debugHookTypes = null;
    if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
      Object.preventExtensions(this);
    }
  }
}

在这里插入图片描述
举个例子:

import React, { Component } from 'react';

class Header extends Component {
    render() {
        return <h1>{this.props.title}</h1>;
    }
}

function Content(props) {
    return (
        <div>
            <p>{props.text}</p>
            <Footer />
        </div>
    );
}

class Footer extends Component {
    render() {
        return <footer>Footer Content</footer>;
    }
}

class App extends Component {
    render() {
        return (
            <div>
                <Header title="Welcome to My App" />
                <Content text="This is some example content." />
            </div>
        );
    }
}

export default App;

Fiber结构:
Root Fiber Node
└── Type: “div” (elementType: “div”)
├── Child Fiber Node
│ ├── Type: “Header” (elementType: Header)
│ │ └── State Node: Header instance
│ │ └── Props: { title: “Welcome to My App” }
│ └── Sibling: Content Fiber Node
└── Child Fiber Node
├── Type: “Content” (elementType: Content)
│ └── State Node: null (函数组件没有状态节点)
│ └── Props: { text: “This is some example content.” }
│ └── Child Fiber Node
│ ├── Type: “Footer” (elementType: Footer)
│ │ └── State Node: Footer instance
│ │ └── Props: {}
│ └── Sibling: null

更新dom

双缓存机制
内存中绘制当前的 fiber dom,绘制完后直接替换上一帧的fiber dom,这样省去两帧之间替换的计算时间,就不会存在白屏的情况,因此就有两棵fiber树

  • current fiber 屏幕上正在显示的内容
  • workingprogress fiber 内存中正在构建的树 简称 wip fiber

alternate 连接

在这里插入图片描述
在这里插入图片描述

mount 构建过程

  • 应用级别的节点 ReactDom.render 创建 fiberRootNode
  • rootFiber 组件树的根节点

在这里插入图片描述

render阶段 — scheduler reconciler

通过遍历 找到所有的fiber结构 实现可中断的异步递归

  1. 递 => 生成树
    创建节点,形成节点之间的关系
    vdom

从 rootfiber 深度优先遍历 fiber 调用 beginwork

  • 根据传入的 fiber 节点创建子 fiber 节点,连接两个 fiber 节点
  • 遍历叶子节点,进入归的阶段

  1. 调用completework
  • 创建真实的dom节点
  • 将当前节点下的子节点挂载到当前节点上
  • 收集当前节点的effectlist

递 归 交错执行,直到归到rootFiber
在这里插入图片描述

react源码解析

react github

react-dom

在这里插入图片描述

react-dom/src/client/ReactDOMRoot.js

在这里插入图片描述
在这里插入图片描述
最终返回ReactDOMRoot这个实例

ReactDOMRoot 和 ReactDOMHydrationRoot 上面都挂载 render 方法
接收children
调用updateContainer方法,传入children,updateContainer来进行递归的这个阶段,创建当前节点
在这里插入图片描述

后面还挂载了unmount方法
在这里插入图片描述

react-reconciler

react-reconciler/src/ReactFiberReconciler.js

这里着重是 Scheduler
创建FiberRootNode节点绑定root
render渲染方法传入children(App),调用updateContainer方法,进行大任务拆分小任务,优先级调度
在这里插入图片描述

react-reconciler/src/ReactFiberWorkLoop.js

着重介绍循环创建fiber树的方法
核心方法:performUnitOfWork,workLoopConcurrent
workLoopSync 同步方法,不会判断 shouldYield,这是和workLoopConcurrent方法的区别
在这里插入图片描述
workLoopConcurrent方法是异步模式,都是调用performUnitOfWork构造fiber树
在这里插入图片描述
这里是Scheduler调度器将渲染任务拆分成不同的任务单元去创建对应的fiber,fiber通过performUnitOfWork去完成fiber单元的创建,然后通过shouldYield判断是否执行这样的任务
workInProgress是全局的变量,存储在全局

performUnitOfWork中记录当前“递”和“归”的一个过程,判断当前满足条件,进入beginWork

Reconciler 协调器阶段,创建fiber树,递归遍历,diff算法比较差异
performUnitOfWork中判断是否是开发环境,开发环境开启性能调优则计算执行时间
调用beginWork进行节点的递阶段,拆解组件内容,并且返回下一个组件
当深度遍历到最底层的时候,开始进行归的阶段
则next为空,调用completeUnitOfWork开始”归“的阶段,归回父节点,更新父节点状态
"递"和"归"阶段是交错执行的,直到回到rootFiber为止
在这里插入图片描述

react-reconciler/src/ReactFiberBeginWork.js

  1. beginWork
    在这里插入图片描述
    if 阶段:后续进入到diff的过程,非首次渲染
    否则为else阶段

后面:
在这里插入图片描述

  1. updateHostComponent
    在这里插入图片描述

  2. reconcileChildren
    mount组件:创建新的子Fiber节点
    在这里插入图片描述

react-reconciler/src/ReactChildFiber.js

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
通过调用useFiber创建fiber节点
在这里插入图片描述

react-reconciler/src/ReactFiber.js

找到workInProgress,为null则创建Fiber节点
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
不论是哪个方法,最终返回的都是 workInProgress.child下一个节点

react-reconciler/src/ReactFiberWorkLoop.js

performUnitOfWork中
上述 beginWork返回的是workInProgress.child下一个节点,因此next就会发生变化
next为null时候,则叶子节点为空,调用completeUnitOfWork
在这里插入图片描述
next不为空,则将next指针赋值给workInProgress,修改workInProgress指向,重新执行beginWork

在 completeUnitOfWork 中,创建对应的dom元素,如果sibling不为null,然后创建对应的指针
在这里插入图片描述

commit阶段 同步阶段

effectlist

  • before mutation 阶段,执行 dom操作前
  • mutation 阶段,执行dom操作阶段,遍历effectlist,执行mutation
  • layout 阶段,执行dom操作后,绘制

可以自己写一个实例,然后打断点看操作数据,操作结果
在这里插入图片描述

一句话来总结commit阶段所作的事情:
基于链表的方式存储副作用,并根据优先级执行这些更新,直至所有的更新完成。

  1. React遍历fiber树并将需要执行副作用的节点以链表的形式收集起来
  2. 根据优先级存储更新
  3. 递归执行更新

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

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

相关文章

【Vue】打包vue3+vite项目发布到github page的完整过程

文章目录 第一步&#xff1a;打包第二步&#xff1a;github仓库设置第三步&#xff1a;安装插件gh-pages第四步&#xff1a;两个配置第五步&#xff1a;上传github其他问题1. 路由2.待补充 参考文章&#xff1a; 环境&#xff1a; vue3vite windows11&#xff08;使用终端即可&…

类加载机制及双亲委派模型

一、引言 二、类加载流程 1. 加载 2. 连接 2.1 验证 2.2 准备 2.3 解析 3. 初始化 三、类加载器 类加载器的类型 双亲委派模型 打破双亲委派模型 双亲委派模型优点 一、引言 在 Java 的运行机制中&#xff0c;类加载是一个至关重要的环节。它不仅决定了 Java 程序的动态…

尚硅谷课程【笔记】——大数据之Hadoop【一】

课程视频链接&#xff1a;尚硅谷Hadoop3.x教程 一、大数据概论 1&#xff09;大数据概念 大数据&#xff08;Big Data&#xff09;&#xff1a;指无法再一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发…

DockerFile优化镜像体积

title: DockerFile优化镜像体积 date: 2025-02-15 15:22:40 tags: DockerFile优化镜像体积DockerFile优化镜像体积 DockerFile优化镜像体积前文回顾:一、细数优化镜像体积的思路与方式二、优化Dockfile文件编辑 Dockerfile2文件三、构建镜像四、运行镜像五、查看运行效果原文 …

题解:洛谷 P4113 [HEOI2012] 采花

题目https://www.luogu.com.cn/problem/P4113 运用类似于P1972 [SDOI2009] HH的项链的操作&#xff0c;将数据离线下来处理。 按照区间右端点从小到大排序。 问题是数量大于等于 的时候才能算进去。 于是乎我们用两个数组维护倒数第二次出现和最后一次出现的地方。 每次在…

easyexcel快速使用

1.easyexcel EasyExcel是一个基于ava的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel 即通过java完成对excel的读写操作&#xff0c; 上传下载 2.easyexcel写操作 把java类中的对象写入到excel表格中 步骤 1.引入依赖 <depen…

android studio下载安装汉化-Flutter安装

1、下载android studio官方地址&#xff1a;&#xff08;这个网址可能直接打不开&#xff0c;需要VPN&#xff09; https://developer.android.com/studio?hlzh-cn mac版本分为X86和arm版本&#xff0c;电脑显示芯片是Inter的就是x86的&#xff0c;显示m1和m2的就是arm的 …

Seaweedfs(master volume filer) docker run参数帮助文档

文章目录 进入容器后执行获取weed -h英文中文 weed server -h英文中文 weed volume -h英文中文 关键点测试了一下&#xff0c;这个-volume.minFreeSpace string有点狠&#xff0c;比如设置值为10&#xff08;10%&#xff09;&#xff0c;它直接给系统只留下10%的空间&#xff0…

【工业安全】-CVE-2022-35555- Tenda W6路由器 命令注入漏洞

文章目录 1.漏洞描述 2.环境搭建 3.漏洞复现 4.漏洞分析 4.1&#xff1a;代码分析  4.2&#xff1a;流量分析 5.poc代码&#xff1a; 1.漏洞描述 漏洞编号&#xff1a;CVE-2022-35555 漏洞名称&#xff1a;Tenda W6 命令注入 威胁等级&#xff1a;高危 漏洞详情&#xff1…

C#(Winform)通过添加AForge添加并使用系统摄像机

先展示效果 AForge介绍 AForge是一个专门为开发者和研究者基于C#框架设计的, 也是NET平台下的开源计算机视觉和人工智能库 它提供了许多常用的图像处理和视频处理算法、机器学习和神经网络模型&#xff0c;并且具有高效、易用、稳定等特点。 AForge主要包括: 计算机视觉与人…

【LeetCode: 611. 有效三角形的个数 + 排序 + 双指针】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

每日十题八股-补充材料-2025年2月15日

1.TCP是如何保证消息的顺序和可靠的&#xff1f; 写得超级好的文章 首先肯定是三次握手和四次挥手保证里通讯双方建立了正确有效的连接。 其次是校验和、序列号&#xff0c;ACK消息应答机制还有重传机制&#xff0c;保证了消息顺序和可靠。 同时配合拥塞机制和流量控制机制&am…

国内已经部署DeepSeek的第三方推荐

大家好&#xff0c;我是苍何。 最近DeepSeek爆火&#xff0c;我也说点心里话&#xff0c;其实就我们普通人而言&#xff0c;要想用好 DeepSeek&#xff0c;其实无非就是要利用好工具为我们自己提效。 比如你是搞编程的&#xff0c;你就得学会如何用 DeepSeek 更快速的辅助你编…

Windows环境下使用Ollama搭建本地AI大模型教程

注&#xff1a;Ollama仅支持Windows10及以上版本。 安装Ollama 去 ollama官网 下载对应平台及OS的安装包。 运行安装包&#xff0c;点击“安装”按钮即可开始安装。Ollama会自动安装到你的 C:\Users\<当前用户名>\AppData\Local\Programs\Ollama 目录上。 安装完成后&…

2024年认证杯SPSSPRO杯数学建模A题(第二阶段)保暖纤维的保暖能力全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 A题 保暖纤维的保暖能力 原题再现&#xff1a; 冬装最重要的作用是保暖&#xff0c;也就是阻挡温暖的人体与寒冷环境之间的热量传递。人们在不同款式的棉衣中会填充保暖材料&#xff0c;从古已有之的棉花、羽绒到近年来各种各样的人造纤维。不…

算法19(力扣244)反转字符串

1、问题 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 2、示例 &#xff08;1&#xff09; 示例 1&a…

DeepSeek 助力 Vue 开发:打造丝滑的卡片(Card)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

ESP32 arduino + DeepSeek API访问

此项目主要使用ESP32-S3实现一个AI语音聊天助手&#xff0c;可以通过该项目熟悉ESP32-S3 arduino的开发&#xff0c;百度语音识别&#xff0c;语音合成API调用&#xff0c;百度文心一言大模型API的调用方法&#xff0c;音频的录制及播放&#xff0c;SD卡的读写&#xff0c;Wifi…

最新国内 ChatGPT Plus/Pro 获取教程

最后更新版本&#xff1a;20250202 教程介绍&#xff1a; 本文将详细介绍如何快速获取一张虚拟信用卡&#xff0c;并通过该卡来获取ChatGPT Plus和ChatGPT Pro。 # 教程全程约15分钟开通ChatGPT Plus会员帐号前准备工作 一个尚未升级的ChatGPT帐号&#xff01;一张虚拟信用卡…

SQLMesh 系列教程4- 详解模型特点及模型类型

SQLMesh 作为一款强大的数据建模工具&#xff0c;以其灵活的模型设计和高效的增量处理能力脱颖而出。本文将详细介绍 SQLMesh 模型的特点和类型&#xff0c;帮助读者快速了解其强大功能。我们将深入探讨不同模型类型&#xff08;如增量模型、全量模型、SCD Type 2 等&#xff0…