React18源码: reconciler执行流程
reconciler执行流程
1 )概述
- 此处先归纳一下react-reconciler包的主要作用,将主要功能分为4个方面:
- 输入:暴露api函数(如:scheduleUpdateOnFiber), 供给其他包(如react包)调用
- 注册调度任务:与调度中心(scheduler包)交互,注册调度任务task,等待任务回调
- 执行任务回调:在内存中构造出fiber树,同时与渲染器(react-dom)交互,在内存中创建出与fiber对应的DOM节点
- 输出:与渲染器(react-dom)交互,渲染DOM节点

- 图中的1,2,3,4步骤可以反映react-reconciler包从输入到输出的运作流程
- 这是一个固定流程,每一次更新都会运行
2 )输入
-
在ReactFiberWorkLoop.js中,承接输入的函数只有scheduleUpdateOnFiber
-
在 react-reconciler 对外暴露的api函数中,只要涉及到需要改变fiber的操作(无论是首次渲染或后续更新操作)
-
最后都会间接调用 scheduleUpdateOnFiber
-
所以scheduleUpdateOnFiber函数是输入链路中的必经之路
//唯一接收输入信号的函数 export function scheduleUpdateOnFiber(fiber: Fiber,lane: Lane,eventTime: number, ) {// ... 省略部分无关代码const root = markUpdateLaneFromFiberToRoot(fiber, lane);// 同步if (lane === SyncLane) {if ((executionContext & LegacyUnbatchedContext) !== NoContext &&(executionContext & (RenderContext | CommitContext)) === NoContext) {// 直接进行fiber构造performSyncWorkOnRoot(root);} else {// 注册调度任务,经过`Scheduler'包的调度,间接进行`fiber构造'ensureRootIsScheduled(root, eventTime);}} else {// 注册调度任务,经过`Scheduler`包的调度,间接进行`fiber构造`ensureRootIsScheduled(root, eventTime);} }
-
逻辑进入到scheduleUpdateOnFiber之后,后面有2种可能:
- 1.不经过调度,直接进行fiber构造.
- 2.注册调度任务,经过Scheduler包的调度,间接进行fiber构造.
2 )注册调度任务
与输入环节紧密相连,scheduleUpdateOnFiber函数之后,立即进入 ensureRootIsScheduled 函数
// ... 省略部分无关代码
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {//前半部分:判断是否需要注册新的调度const existingCallbackNode - root. callbackNode;const nextlanes = getNextLanes(root,root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,);const newCallbackPriority = returnNextLanesPriority();if (nextLanes === NoLanes) {return;}if (existingCallbackNode !== null) {const existingCallbackPriority = root.callbackPriority;if (existingCallbackPriority === newCallbackPriority) {return;}cancelCallback(existingCallbackNode);}// 后半部分:注册调度任务let newCallbackNode;if (newCallbackPriority === SyncLanePriority){newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root),);} else if (newCallbackPriority === SyncBatchedLanePriority) {newCallbackNode = scheduleCallback(ImmediateSchedulerPriority,performSyncWorkOnRoot.bind(null, root),);} else {const schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority,);newCallbackNode = scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root),);}root.callbackPriority = newCallbackPriority;root.callbackNode = newCallbackNode;
}
- ensureRootIsScheduled的逻辑很清晰,分为2部分:
- 1.前半部分:判断是否需要注册新的调度(如果无需新的调度,会退出函数)
- 2.后半部分:注册调度任务
- performSyncWorkOnRoot 或 performConcurrentWorkOnRoot 被封装到了任务回调 (schedulecallback)
- 等待调度中心执行任务,任务运行其实就是执行 performSyncWorkOnRoot 或 performConcurrentWorkOnRoot
3 )执行任务回调
-
任务回调,实陈上就是执行 performSyncWorkOnRoot 或 performConcurrentWorkOnRoot
-
简单看一下它们的源码将主要逻辑剥离出来,单个函数的代码量并不多
//..,省略部分无关代码 function performSyncWorkOnRoot(root) {let lanes;let exitStatus;lanes = getNextLanes(root, NoLanes);// 1. fiber树构造exitStatus = renderRootSync(root, lanes);// 2. 异常处理:有可能fiber构造过程中出现异常if (root.tag !== LegacyRoot && exitStatus === RootErrored) {// ...}// 3. 输出:渲染fiber树const finishedWork: Fiber = (root.current.alternate: any);root.finishedwork = finishedWork;root.finishedLanes = lanes;commitRoot(root);// 退出前再次检测,是否还有其他更新,是否需要发起新调度ensureRootIsScheduled(root, now());return null; }
-
performSyncWorkOnRoot 的逻辑很清晰,分为3部分:
-
fiber 树构造
-
异常处理: 有可能fiber构造过程中出现异常
-
调用输出
// ... 省略部分无关代码 function performConcurrentWorkOnRoot(root) {const originalCallbackNode = root.callbackNode;// 1、刷新pending状态的effects,有可能某些effect会取消本次任务const didFlushPassiveEffects = flushPassiveEffects();if (didFlushPassiveEffects) {if (root.callbackNode !== originalCallbackNode) {// 任务被取消,退出调用return null;} else {// Current task was not canceled. Continue.}}// 2.获取本次渲染的优先级let lanes = getNextLanes(root,root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,);// 3.构造fiber树let exitStatus = renderRootConcurrent(root, lanes);if (includesSomeLane(workInProgressRootIncludedLanes,workInProgressRootUpdatedLanes,)) {// 如果在render过程中产生了新的update,且新update的优先级与最初render的优先级有交集// 那么最初render无效,丢弃最初render的结果,等待下一次调度prepareFreshStack(root, NoLanes);} else if (exitStatus !== RootIncomplete) {// 4、异常处理:有可能fiber构造过程中出现异常if (exitStatus == RootErrored) {// ...}const finishedWork: Fiber = (root.current. alternate: any);root.finishedWork = finishedwork;root.finishedLanes = lanes;// 5.输出:渲染fiber树finishConcurrentRender(root, exitStatus, lanes);}// 退出前再次检测,是否还有其他更新,是否需要发起新调度ensureRootIsScheduled(root, now());if (root.callbackNode === originalCallbackNode) {// 渲染被阻断,返回一个新的performConcurrentWorkOnRoot函数。等待下一次调用return performConcurrentWorkOnRoot.bind(null, root);}return null; }
-
-
performConcurrentWorkOnRoot 的逻辑与 performSyncWorkOnRoot 的不同之处在于
-
对于可中断渲染的支持:
- 1.调用 performConcurrentWorkOnRoot 函数时,首先检查是否处于 render 过程中,是否需要恢复上一次渲染
- 2.如果本次渲染被中断,最后返回一个新的 performConcurrentWorkOnRoot 函数,等待下一次调用
4 )输出
// ... 省略部分无关代码
function commitRootImpl(root, renderPriorityLevel) {// 设置局部变量const finishedWork = root.finishedWork;const lanes - root. finishedLanes;// 清空FiberRoot对象上的属性root.finishedWork = null;root.finishedLanes = NoLanes;root.callbackNode = null;// 提交阶段let firstEffect = finishedWork.firstEffect;if (firstEffect !== null) {const prevExecutionContext - executionContext;executionContext |= CommitContext;// 阶段1:dom突变之前nextEffect = firstEffect;do {commitBeforeMutationEffects();} while (nextEffect !== null);// 阶段2:dom突变,界面发生改变nextEffect = firstEffect;do {commitMutationEffects(root, renderPriorityLevel);} while (nextEffect !== null);root.current = finishedWork;// 阶段3:layout阶段,调用生命周期componentDidUpdate和回调函数等nextEffect = firstEffect;do{commitLayoutEffects(root, lanes);} while (nextEffect !== null);nextEffect = null;executionContext = prevExecutionContext;}ensureRootIsScheduled(root, now());return null;
}
- 在输出阶段,commitRoot 的实现逻辑是在 commitRootImpl 函数中
- 其主要逻辑是处理副作用队列,将最新的fiber树结构反映到DOM上
- 核心逻辑分为3个步骤:
- 1.commitBeforeMutationEffects
- dom变更之前,主要处理副作用队列中带有Snapshot, Passive标记的fiber节点
- 2.commitMutationEffects
- dom变更,界面得到更新.主要处理副作用队列中带有
- Placement,Update,Deletion, Hydrating标记的fiber节点
- 3.commitLayoutEffects
- dom变更后,主要处理副作用队列中带有 update | Callback 标记的fiber节点.
- 1.commitBeforeMutationEffects
- 这块流程参考 React16版本的流程,看下不同之处
- 参考: https://blog.csdn.net/Tyro_java/article/details/135845906
- 所以,整个 reconciler 的执行过程中,核心做了2个事情
- 1 )Render (基于task, 可以被打断, 可以被打断的前提是基于渲染 mode)
- 初始化 fiber
- 更新 fiber
- 2 )commit
- dom 变更之前
- dom 变更
- dom 更新之后
- 1 )Render (基于task, 可以被打断, 可以被打断的前提是基于渲染 mode)
相关文章:

React18源码: reconciler执行流程
reconciler执行流程 1 )概述 此处先归纳一下react-reconciler包的主要作用,将主要功能分为4个方面: 输入:暴露api函数(如:scheduleUpdateOnFiber), 供给其他包(如react包࿰…...

mapbox面图层标注
mapbox并没有一个属性类似于’text-field’的symbol图层的直接可以标注的办法,这里笔者提供两种其他的面图层标注的办法用来大家参考 效果图 方案一 把面图层当做点图层直接展示 在mapbox里面,面图层是可以直接渲染成线图层和点图层的,这里…...

MySQL|MySQL基础(求知讲堂-学习笔记【详】)
MySQL基础 目录 MySQL基础一、 MySQL的结构二、 管理数据库1)查询所有的数据库2)创建数据库3)修改数据库的字符编码4)删除数据库5)切换操作的数据库 三、表的概念四、字段的数据类型4.1 整型4.2 浮点型(float和double)…...

10.docker exec -it /bin/bash报错解决、sh与bash区别
报错 进入容器时,报如下错误 dockeruserdell-PowerEdge-R740:~$ docker exec -it daf2 /bin/bash OCI runtime exec failed: exec failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown…...

查询数据库的编码集Oracle,MySQL
1、查询数据库的编码集Oracle,MySQL 1.1、oracle select * from v$nls_parameters where parameterNLS_CHARACTERSET; 查询版本:SELECT * FROM v$version 2、MySQL编码集 SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SC…...

电商数据采集+跨境电商|API电商数据采集接口洞悉数字新零售发展
随着全球经济一体化和电子商务的快速发展,网络购物的需求日益增加。不断涌现的电商企业使得行业的竞争情况愈演愈烈。在这种情况下,企业不仅要加大经营力度,还要在自己的基础设施和技术上持续投入,才能更好的适应市场和消费习惯。…...

linux之用户和用户组
文章目录 一、简介1.1 用户1.2 用户组1.3 UID和GID1.4 用户账户分类 二、用户2.1 添加新的用户账号:useradd2.2 删除账号:userdel2.3 修改账号:usermod(modmodify)2.4 用户口令的管理:passwd2.5 切换用户:su 三、用户组3.1 增加一…...

人工智能深度学习
目录 人工智能 深度学习 机器学习 神经网络 机器学习的范围 模式识别 数据挖掘 统计学习 计算机视觉 语音识别 自然语言处理 机器学习的方法 回归算法 神经网络 SVM(支持向量机) 聚类算法 降维算法 推荐算法 其他 机器学习的分类 机器…...

python reshape 和 transpose的区别
reshape() 和 transpose() 是用于改变数组或张量形状的两种不同方法, 它们的主要区别在于如何重新排列元素以及是否可以改变轴的顺序。 1 reshape() reshape() 函数用于改变数组或张量的形状,但是不改变元素的排列顺序。它只是简单地将数组的维度重新…...

音视频技术-网络视频会议“回声”的消除
目录 一、“回音”的成因原理 二、解决思路 三、解决方案 1、方案一 2...

有哪些令人惊讶的心理学效应
大家可以想象一个场景: 如果一次考试,你考了95分,比上次还进步了10分,你会感到高兴吗? 听起来很牛逼啊,值得干杯庆祝,好好开心几天了。 这时,你看到同桌这次居然是一百分…...

二叉树基础知识总结
目录 二叉树基础知识 概念 : 根节点的五个形态 : 特殊的二叉树 满二叉树 : 完全二叉树 : 二叉搜索树 : 平衡二叉搜索树 : 二叉树的性质 : 二叉树的存储结构 二叉树的顺序存储结构 二叉树的链式存储结构 二叉树的遍历方式 : 基础概念 前中后遍历 层序遍历 :…...

IDEA2023.3.4开启SpringBoot项目的热部署【简单明了4步操作】
添加devtools依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional> </dependency>IDEA开启自动编译 …...

QT中调用python
一.概述 1.Python功能强大,很多Qt或者c/c开发不方便的功能可以由Python编码开发,尤其是一些算法库的应用上,然后Qt调用Python。 2.在Qt调用Python的过程中,必须要安装python环境,并且Qt Creator中编译器与Python的版…...

Sora基础知识学习
...

开源博客项目Blog .NET Core源码学习(9:Autofac使用浅析)
开源博客项目Blog使用Autofac注册并管理组件和服务,Autofac是面向.net 的开源IOC容器,支持通过接口、实例、程序集等方式注册组件和服务,同时支持属性注入、方法注入等注入方式。本文学习并记录Blog项目中Autofac的使用方式。 整个Blog解…...

Go语言中的TLS加密:深入crypto/tls库的实战指南
Go语言中的TLS加密:深入crypto/tls库的实战指南 引言crypto/tls库的核心组件TLS配置:tls.Config证书加载与管理TLS握手过程及其实现 构建安全的服务端创建TLS加密的HTTP服务器配置TLS属性常见的安全设置和最佳实践 开发TLS客户端应用编写使用TLS的客户端…...

网络原理-TCP/IP(7)
目录 网络层 路由选择 数据链路层 认识以太网 以太网帧格式 认识MAC地址 对比理解MAC地址和IP地址 认识MTU ARP协议 ARP协议的作用 ARP协议工作流程 重要应用层协议DNS(Domain Name System) DNS背景 NAT技术 NAT IP转换过程 NAPT NAT技术的优缺点 网络层 路由…...

HarmonyOS4.0系列——08、整合UI常用组件
HarmonyOS4.0 系列——08、UI 组件 Blank Blank 组件在横竖屏占满空余空间效果 // xxx.ets Entry Component struct BlankExample {build() {Column() {Row() {Text(Button).fontSize(18)Blank()Toggle({type: ToggleType.Switch}).margin({top: 14,bottom: 14,left: 6,righ…...

【Spring Boot 3】【JPA】一对多单向关联
【Spring Boot 3】【JPA】一对多单向关联 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花…...

工信部等九部门:打造一批实现制造过程数字孪生的数字化转型标杆工厂
“人工智能技术与咨询” 发布 培育一批科技领军人才、青年骨干人才,以及一批既懂原材料工业又懂数字技术的复合型人才。依托职业教育提质培优行动计划,加速培育数字化转型急需紧缺的工程师和技术技能人才。支持引进数字化转型海外高端人才。 ÿ…...

并发编程(2)基础篇-管程
4 共享模型之管程 本章内容 共享问题synchronized线程安全分析Monitorwait/notify线程状态转换活跃性Lock 4.1 共享带来的问题 4.1.1 小故事 老王(操作系统)有一个功能强大的算盘(CPU),现在想把它租出去ÿ…...

OpenAI文生视频大模型Sora概述
Sora,美国人工智能研究公司OpenAI发布的人工智能文生视频大模型(但OpenAI并未单纯将其视为视频模型,而是作为“世界模拟器” ),于2024年2月15日(美国当地时间)正式对外发布。 Sora可以根据用户…...

[linux]进程间通信(IPC)———共享内存(shm)(什么是共享内存,共享内存的原理图,共享内存的接口,使用演示)
一、什么是共享内存 共享内存区是最快的(进程间通信)IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。注意:…...

Go 原子操作有哪些?
Go atomic包是最轻量级的锁(也称无锁结构),可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作,不过这个包只支持int32/int64/uint32/uint64/uintptr这几种数据类型的一些基础操作(增减、交换、载入、存…...

爬虫知识--02
免费代理池搭建 # 代理有免费和收费代理 # 代理有http代理和https代理 # 匿名度: 高匿:隐藏访问者ip 透明:服务端能拿到访问者ip 作为后端,如何拿到使用代理人的ip 请求头中:x-forwor…...

SCI一区 | Matlab实现GAF-PCNN-MSA格拉姆角场和双通道PCNN融合注意力机制的多特征分类预测
SCI一区 | Matlab实现GAF-PCNN-MSA格拉姆角场和双通道PCNN融合注意力机制的多特征分类预测 目录 SCI一区 | Matlab实现GAF-PCNN-MSA格拉姆角场和双通道PCNN融合注意力机制的多特征分类预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 1.【SCI一区级】Matlab实…...

Observability:使用 OpenTelemetry 和 Elastic 监控 OpenAI API 和 GPT 模型
作者: 来自 Elastic David Hope ChatGPT 现在非常火爆,甚至席卷了整个互联网。 作为 ChatGPT 的狂热用户和 ChatGPT 应用程序的开发人员,我对这项技术的可能性感到非常兴奋。 我看到的情况是,基于 ChatGPT 的解决方案将会呈指数级…...

靡语IT:Vue精讲(一)
Vue简介 发端于2013年的个人项目,已然成为全世界三大前端框架之一,在中国大陆更是前端首选。 它的设计思想、编码技巧也被众多的框架借鉴、模仿。 纪略 2013年,在Google工作的尤雨溪,受到Angular的启发,从中提取自…...

vue3 toRefs之后的变量修改方法
上效果 修改值需要带上解构之前的对象名obj, changeName:()>{ // toRefs 解决后变量修改值方法: 解构前变量.字段新值 obj.name FEIFEI; } } 案例源码 <!DOCTYPE html> <html> <head><me…...