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

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 yieldwhile (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; // 上次渲染时使用的属性值 相当于是旧值oldValuethis.updateQueue = null;this.memoizedState = null;this.dependencies = null;this.mode = mode;// 记录变化的节点 effectlist=>renderthis.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在另一次更新时对应的fiberthis.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=8538this.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.rhkb.cn/news/19357.html

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

相关文章

ubuntu20.04连接airpods pro2

youtub视频解决方法&#xff1a;Setup your AirPods Pro with Linux (Ubuntu w/Mic fix) 重要步骤1&#xff0c; sudo apt-get update sudo apt-get install pulseaudio sudo apt-get upgrade pluseaudio重要步骤2&#xff0c; sudo vim /etc/bluetooth/main.confsudo /ect/…

sqli_labs_master Less-8 时间盲注,boolen盲注 获取数据库中的表、列

获取当前数据库名 import requestsdef inject_database(url):namemax_length20 # 假设数据库名称最大长度为20# ASCII范围&#xff1a;数字、字母、下划线&#xff08;_&#xff09;low{a: 97, z: 122, A: 65, Z: 90, 0: 48, 9: 57, _: 95}high{97: a, 122: z, 65: A, 90: Z,…

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_ssl_init 函数

#if (NGX_OPENSSL)ngx_ssl_init(log); #endif objs/ngx_auto_config.h 中 #ifndef NGX_OPENSSL #define NGX_OPENSSL 1 #endif 所以这个条件编译成立 NGX_OPENSSL 是一个宏定义&#xff0c;用于控制与 OpenSSL 相关的功能是否被启用 若用户通过./configure参数&#xff08;如-…

常用架构图:业务架构、产品架构、系统架构、数据架构、技术架构、应用架构、功能架构及信息架构

文章目录 引言常见的架构图I 业务架构图-案例模块功能说明1. 用户界面层 (UI)2. 应用服务层3. 数据管理层4. 基础设施层业务流程图示例技术实现II 功能架构图 -案例功能模块说明1. 船舶监控模块2. 报警管理模块3. 应急响应模块4. 通信管理模块5. 数据分析模块数据管理层基础设施…

Redis 09章——哨兵(sentinel)

一、是什么 吹哨人巡查监控后台master主机是否故障&#xff0c;如果故障了根据$\textcolor{red}{投票数}$自动将某一个从库转换为新主库&#xff0c;继续对外服务作用&#xff1a;俗称无人值守运维官网理论&#xff1a;High availability with Redis Sentinel | Docs 二、能干…

WEB入门

1.了解什么是WEB 2.了解什么是WEB前端 3.学习和掌握开发环境的搭建 4.网页开发基础 1.什么是WEB 1946年&#xff0c;有一个发明了人类史上第一台完整体系的计算机 时间进展到1989年&#xff0c;实验室有一个研发人员 TimBernersLee&#xff08;提姆博纳斯李&#xff09;开…

【若依免登录】登录状态已过期,您可以继续留在该页面,或者重新登录

【问题】 【问题背景】 在实现若依的免登录出现的问题。前面已经做的操作 前端 第一步&#xff1a;views目录下面存在需要免登录的页面 第二步&#xff1a;在ruoyi-ui\src\router 的 index.js 的 constantRoutes &#xff08;公共路由方&#xff09;中 配置需要免登录的页面位…

JUC并发-4.wait和notify以及Atomic原理

大纲 1.wait()与notify()实现一个简易的内存队列 2.wait()与notify()的底层原理 3.分布式存储系统NameNode机制介绍 4.分布式存储系统的edits log机制介绍 5.分布式存储系统的NameNode实现 6.分布式存储系统的创建目录功能的实现 7.edits log的全局txid机制和双缓冲机制…

Jasper AI技术浅析(二):语言模型

Jasper AI 的核心语言模型是基于 OpenAI 的 GPT-4(Generative Pre-trained Transformer 4)构建的。GPT-4 是一种生成式预训练语言模型,利用深度学习技术,特别是 Transformer 架构,来理解和生成自然语言。 1. GPT-4 的基本原理 1.1 Transformer 架构 GPT-4 基于 Transfo…

(arxiv2411) CARE Transformer

作者提出了两个问题&#xff0c;问题 1&#xff1a;堆叠是充分利用局部归纳偏差和长距离信息优势的最佳方法吗&#xff1f; 问题 2&#xff1a;是否有可能同时提高线性视觉 Transformer 的效率和准确性&#xff1f; 为了解决这两个问题&#xff0c;作者提出了一种 deCoupled du…

RISC-V平台编译 state-thread x264 ffmpeg zlog

1.state-threads 源码下来之后 直接 make linux-debug 目录下生成了对应的.a 和 .h文件 gcc test.c -o test -l st -L . #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/socket.h&g…

DeepSeek笔记(二):DeepSeek局域网访问

如果有多台电脑&#xff0c;可以通过远程访问&#xff0c;实现在局域网环境下多台电脑共享使用DeepSeek模型。在本笔记中&#xff0c;首先介绍设置局域网多台电脑访问DeepSeek-R1模型。 一、启动Ollama局域网访问 1.配置环境变量 此处本人的操作系统是Windows11&#xff0c;…

2025年二级建造师报名流程图解

2025年二级建造师报名时间&#xff01;附报名流程&#xff01; ⏰️已公布25年二建考试时间的省份如下&#xff1a; ️4月19日、20日考试的城市有&#xff1a;贵州 ️5月10日、11日考试的城市有&#xff1a;湖北、陕西、宁夏、甘肃、福建、浙江、江西、黑龙江、河南、湖南、…

Android的Activity生命周期知识点总结,详情

一. Activity生命周期 1.1 返回栈知识点 二. Activity状态 2.1 启动状态 2.2 运行状态 2.3 暂停状态 2.4 停止状态 2.5 销毁状态 三. Activity生存期 3.1 回调方法 3.2 生存期 四. 体验Activity的生命周期 五. Activity被回收办法 引言&#xff1a; 掌握Acti…

frp-tool,客户端frp命令行工具

在日常开发和运维过程中&#xff0c;端口转发和配置管理是常见的需求。 如果有自己一台服务器&#xff0c;并且已经开放好端口&#xff0c;配置好token后&#xff0c;这个工具一定能帮到你。 今天给大家推荐一款非常好用的frpc命令行工具&#xff0c;它是一个用Python编写的命令…

netcore https配置

一、生成证书 1. 安装 OpenSSL 如果尚未安装 OpenSSL&#xff0c;可以通过以下命令安装&#xff1a;Ubuntu/Debian:sudo apt update sudo apt install openssl CentOS/RHEL:sudo yum install openssl 2. 生成私钥 使用以下命令生成私钥文件&#xff08;private.key&#xff09…

解锁ApplicationContext vs BeanFactory: 谁更具选择性?

目录 一、聚焦源码回顾 &#xff08;一&#xff09;源码分析和理解 &#xff08;二&#xff09;简短的回顾对比建议 二、ApplicationContext vs BeanFactory特性对比 &#xff08;一&#xff09;主要特性总结 &#xff08;二&#xff09;直接建议 三、案例简单说明 &am…

【大模型】DeepSeek 高级提示词技巧使用详解

目录 一、前言 二、DeepSeek 通用提示词技巧 2.1 DeepSeek 通用提示词技巧总结 三、DeepSeek 进阶使用技巧 3.1 DeepSeek一个特定角色的人设 3.1.1 为DeepSeek设置角色操作案例一 3.1.2 为DeepSeek设置角色操作案例二 3.2 DeepSeek开放人设升级 3.2.1 特殊的人设&#…

1-2 gitee创建远程仓库

如何创建远程仓库&#xff1f; 第一步点击加号&#xff0c;创建仓库 点击创建仓库给新创建的仓库命名即可 仓库创建成功即可

浏览器打开Axure RP模型

1&#xff0c;直接使用chrome打开&#xff0c;提示下载插件 2&#xff0c;需要做一些操作 打开原型文件&#xff0c;找到resources\chrome\axure-chrome-extension.crx文件&#xff0c;这就是我们需要的Chrome插件。 将axure-chrome-extension.crx文件后缀名改为axure-chrome…