React18源码: Fiber树中的优先级与帧栈模型
优先级{#lanes}
- 在全局变量中有不少变量都以Lanes命名
- 如workInProgressRootRenderLanes, subtreeRenderLanes其作用见上文注释
- 它们都与优先级相关
- React中有3套优先级体系,并了解了它们之间的关联关系
- 现在来看下fiber树构造过程中,车道模型Lane的具体应用
- 在整个react-reconciler包中,Lane的应用可以分为3个方面:
1 ) update优先级(update.lane){#update-lane}
-
update对象,它是一个环形链表.
-
对于单个update对象来讲,update.lane代表它的优先级,称之为update优先
-
观察其构造函数, 其优先级是由外界传入
export function createUpdate(eventTime: number, lane: Lane): Update<*> {const update: Update<*> = {eventTime,lane,tag: UpdateState,payload: null,callback: null,next: null,}return update; } -
在React体系中,有2种情况会创建update对象:
-
1.应用初始化:在react-reconciler包中的updateContainer函数中
export function updateContainer(element: ReactNodelist,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function, ):Lane {const current = container.current;const eventTime = requestEventTime();const lane = requestUpdateLane(current); //根据当前时间,创建一个update优先级const update = createUpdate(eventTime, lane); //lane被用于创建update对象update.payload = { element };enqueueUpdate(current, update);scheduleUpdateOnFiber(current, lane, eventTime);return lane; } -
发起组件更新: 假设在 class 组件中调用 setState
const classComponentUpdater = {isMounted,enqueueSetState(inst, payload, callback) {const fiber = getInstance(inst);const eventTime = requestEventTime(); // 根据当前时间,创建一个update优先级const lane = requestUpdateLane(fiber); // lane被用于创建update对象const update = createUpdate(eventTime, lane);update.payload = payload;enqueueUpdate(fiber, update);scheduleUpdateOnFiber(fiber, lane, eventTime);}, }; -
可以看到,无论是应用初始化或者发起组件更新,创建update.lane的逻辑都是一样的
-
都是根据当前时间,创建一个update优先级
export function requestUpdateLane(fiber: Fiber): Lane {// Special casesconst mode = fiber. mode;if ((mode & BlockingMode) === NoMode) {// legacy 模式return (SyncLane: Lane);} else if ((mode & ConcurrentMode) === NoMode) {// blocking 模式return getCurrentPrioritylevel() === ImmediateSchedulerPriority? (Synclane: Lane): (SyncBatchedLane: Lane);}//concurrent模式if (currentEventWipLanes === NoLanes) {currentEventWipLanes = workInProgressRootIncludedLanes;}const isTransition = requestCurrentTransition() !== NoTransition;if(isTransition) {// 特殊情况,处于suspense过程中if (currentEventPendingLanes !== NoLanes) {currentEventPendingLanes =mostRecentlyUpdatedRoot !== null? mostRecentlyUpdatedRoot. pendingLanes: NoLanes;}return findTransitionLane(currentEventWipLanes, currentEventPendingLanes);}// 正常情况,获取调度优先级const schedulerPriority = getCurrentPriorityLevel();let lane;if ((executionContext & DiscreteEventContext) !== NoContext &&schedulerPriority === UserBlockingSchedulerPriority){// executionContext 存在输入事件,且调度优先级是用户阻塞性质lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes);} else {// 调度优先级转换为车道模型const schedulerLanePriority = schedulerPriorityToLanePriority(schedulerPriority,);lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);}return lane; } -
可以看到requestUpdateLane的作用是返回一个合适的update优先级.
- 1.legacy模:返回SyncLane
- 2.blocking模式:返回SyncLane
- 3.concurrent模式:
- 正常情况下,根据当前的调度优先级来生成一个1ane.
- 特殊情况下(处于suspense过程中),会优先选择TransitionLanes通道中的空闲通道
- 如果所有TransitionLanes通道都被占用,就取最高优先级
-
最后通过
scheduleUpdateOnFiber(current, lane, eventTime);函数 -
把 update.lane正式带入到了输入阶段
-
scheduleUpdateOnFiber是输入阶段的必经函数,此处以 update.lane 的视角分析:export function scheduleUpdateOnFiber(fiber: Fiber,lane:Lane,eventTime: number, ) {if(lane === SyncLane) {// legacyblocking模式if ((executionContext & LegacyUnbatchedContext) !== NoContext &&(executionContext & (RenderContext CommitContext)) === NoContext) {performSyncWorkOnRoot(root);} else {ensureRootIsScheduled(root,eventTime); // 注册回调任务if (executionContext === NoContext) {flushSyncCallbackQueue(); // 取消schedule调度,主动刷新回调队列,}}} else {// concurrent模式ensureRootIsScheduled(root, eventTime);} } -
当lane==SyncLane也就是legacy或blocking模式中,注册完回调任务之后
-
(ensureRootIsScheduled(root, eventTime)),如果执行上下文为空
-
会取消schedule调度,主动刷新回调队列flushsyncCallbackQueue()
-
这里包含了一个热点问题(setState到底是同步还是异步)的标准答案:
- 如果逻辑进入 flushSyncCallbackQueue(executionContext == NoContext)
- 则会主动取消调度,并刷新回调,立即进入fiber树构造过程
- 当执行setState下一行代码时,fiber树已经重新渲染了,故setState体现为同步
- 正常情况下,不会取消schedule调度
- 由于schedule调度是通过MessageChannel触发(宏任务),故体现为异步
- 如果逻辑进入 flushSyncCallbackQueue(executionContext == NoContext)
2 ) 渲染优先级(renderLanes)
-
这是一个全局概念,每一次render之前,首先要确定本次render的优先级,具体对应到源码如下:
//...省略无关代码 function performSyncWorkOnRoot(root) {let lanes;let exitStatus;//获取本次`render'的优先级lanes = getNextLanes(root, lanes);exitStatus = renderRootSync(root, lanes); } //...省略无关代码 function performConcurrentWorkOnRoot(root) {//获取本次`render`的优先级let lanes = getNextLanes(root,root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes,);if (lanes === NoLanes){return null;}let exitStatus = renderRootConcurrent(root, lanes); } -
可以看到,无论是 Legacy 还是 Concurrent 模式,在正式 render 之前,都会调用 getNextLanes 获取一个优先级
//...省略部分代码 export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {// 1. check是否有等待中的lanesconst pendingLanes = root.pendingLanes;if (pendinglanes === NoLanes) {return_highestLanePriority = NoLanePriority;return NoLanes;}let nextLanes = NoLanes;let nextLanePriority = NoLanePriority;const expiredLanes = root.expiredLanes;const suspendedLanes = root.suspendedLanes;const pingedLanes = root.pingedLanes;// 2.check是否有已过期的Lanesif (expiredlanes !== NoLanes) {nextLanes = expiredLanes;nextlanePriority = return_highestLanePriority = SynclanePriority;} else {const nonIdlePendingLanes = pendingLanes & NonIdleLanes;if (nonIdlePendingLanes !== NoLanes) {//非Idle任务...} else {//Idle任务...}}if (nextLanes == NoLanes) {return NoLanes;}return nextLanes; } -
getNextLanes 会根据 fiberRoot 对象上的属性(expiredLanes, suspendedLanes, pingedLanes等)
-
确定出当前最紧急的1anes
-
此处返回的lanes会作为全局渲染的优先级,用于fiber树构造过程中
-
针对fiber对象或update对象,只要它们的优先级(如:fiber.lanes和update.lane)比渲染优先级低,都将会被忽略
3 ) fiber优先级(fiber.lanes)
- 介绍过fiber对象的数据结构.其中有2个属性与优先级相关:
-
1.fiber.lanes
- 代表本节点的优先级
-
2.fiber.childLanes
- 代表子节点的优先级从FiberNode的构造函数中可以看出
- fiber.lanes 和 fiber.childLanes的初始值都为NoLanes
- 在fiber树构造过程中,使用全局的渲染优先级 ( renderLanes)和 fiber.lanes 判断 fiber 节点是否更新.
- 如果全局的渲染优先级rendertanes不包括fiber.lanes
- 证明该fiber节点没有更新,可以复用.
- 如果不能复用,进入创建阶段
function beginWork(current: Fiber| null,workInProgress: Fiber,renderLanes: Lanes, ): Fiber | null {const updatelanes = workInProgress.lanes;if(current !== null) {const oldProps = current.memoizedProps;const newProps = workInProgress.pendingProps;if(oldProps !== newProps ||hasLegacyContextChanged()// Force a re-render if the implementation changed due to hot reload:(_DEV__ ? workInProgress.type !== current. type : false)) {didReceiveUpdate = true;} else if (!includesSomeLane(renderLanes, updateLanes)) {didReceiveUpdate = false;//本`fiber`节点的没有更新,可以复用,进入bailout逻辑return bailoutOnAlreadyFinishedwork(current, workInProgress, renderlanes);}}// 不能复用,创建新的fiber节点workInProgress.lanes = NoLanes;//重优为 NoLanesswitch (workInProgress.tag) {case ClassComponent: {const Component = workInProgress.type;const unresolvedProps = workInProgress. pendingProps;const resolvedProps =workInProgress.elementType === Component? unresolvedProps: resolveDefaultProps(Component, unresolvedProps);return updateClassComponent(current,workInProgress,Component,resolvedProps,// 正常情况下渲染优先级会被用于fier树的构透过程renderLanes,);}} }
-
栈帧管理
-
在React源码中,每一次执行fiber树构造
- 也就是调用performSyncWorkOnRoot或者performConcurrentWorkOnRoot函数的过程
- 都需要一些全局变量来保存状态
-
如果从单个变量来看,它们就是一个个的全局变量.
-
如果将这些全局变量组合起来,它们代表了当前fiber树构造的活动记录
-
通过这一组全局变量,可以还原fiber树构造过程
-
比如时间切片的实现过程fiber树构造过程被打断之后需要还原进度,全靠这一组全局变量
-
所以每次fiber树构造是一个独立的过程,需要独立的一组全局变量
-
在React内部把这一个独立的过程封装为一个栈帧stack
-
简单来说就是每次构造都需要独立的空间
-
所以在进行fiber树构造之前,如果不需要恢复上一次构造进度,都会刷新栈帧(源码在prepareFreshStack函数)
function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {const prevExecutionContext = executionContext;executionContext |= RenderContext;const prevDispatcher = pushDispatcher();// 如果fiberRoot变动,或者update.lane变动,都会刷新栈帧,丢弃上一次渲染进度if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {resetRenderTimer();// 刷新找帧prepareFreshStack(root, lanes);startWorkOnPendingInteractions(root, lanes);} }// 刷新栈帧;重置FiberRoot上的全局属性和`fiber树构造'循环过程中的全局变量 function prepareFreshStack(root: FiberRoot, lanes: Lanes) {// 重置FiberRoot对象上的属性root.finishedWork = null;root.finishedLanes = NoLanes;const timeoutHandle = root.timeoutHandle;if(timeoutHandle !== noTimeout) {root.timeoutHandle = noTimeout;cancelTimeout(timeoutHandle);}if (workInProgress !== null) {let interruptedWork = workInProgress.return;while (interruptedWork !== null){unwindInterruptedWork(interruptedWork);interruptedWork =interruptedWork.return;}}// 重置全局变量workInProgressRoot = root;workInProgress = createWorkInProgress(root.current, null); // 给HostRootFiber对象创建一个alternateworkInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = laneworkInProgressRootExitStatus = RootIncomplete;workInProgressRootFatalError = null;workInProgressRootSkippedlanes = NoLanes; } -
注意其中的
createWorkInProgress(root.current, null) -
其参数
root.current即HostRootFiber -
作用是给
HostRootFiber创建一个 alternate副本 -
workInProgress 指针指向这个副本, 即
workInProgress = HostRootFiber.alternate -
在前文 double buffering 中分析过,HostRootFiber.alternate 是正在构造的fiber树的根节点
相关文章:
React18源码: Fiber树中的优先级与帧栈模型
优先级{#lanes} 在全局变量中有不少变量都以Lanes命名 如workInProgressRootRenderLanes, subtreeRenderLanes其作用见上文注释它们都与优先级相关 React中有3套优先级体系,并了解了它们之间的关联关系现在来看下fiber树构造过程中,车道模型Lane的具体应…...
Hive 最全面试题及答案(基础篇)
基本知识 hive元数据存储 Hive 元数据存储了关于表、分区、列、分桶等信息。 在生产环境中,通常会将 Hive 的元数据存储在外部的关系型数据库中,如 MySQL 或 PostgreSQL。这样可以提供更好的性能、可扩展性和容错性。通过配置 Hive 的元数据存储为 MySQL 或 PostgreSQL,可以…...
【力扣】整数反转,判断是否溢出的数学解法
整数反转原题地址 方法一:数学 反转整数 如何反转一个整数呢?考虑整数操作的3个技巧: xmod10 可以取出 x 的最低位,如 x123 , xmod103 。x/10 可以去掉 x 的最低位,如 x123 , x/10 …...
Jmeter之内置函数__property和__P的区别
1. __property函数 作用 读取 Jmeter 属性 语法格式 ${__property(key,var,default)} 参数讲解 小栗子 ${__property(key)} 读取 key 属性如果找不到 key 属性,则返回 key(属性名) ${__property(key,,default)} 读取 key 属性如果找不到 k…...
GPT润色指令
1. GPT润色指令 Below is a paragraph from an academic paper. Polish the writing to meet the academic style,improve the spelling, grammar, clarity, concision and overall readability. When necessary, rewrite the whole sentence. Paragraph :你的句子…...
Ubuntu中matplotlib显示中文的方法
其实有很多朋友已经总结得很好了:Ubuntu下让matplotlib显示中文字体_ubuntu matplot 使用汉字-CSDN博客 这里我就是简单补充一下: 按照上面这篇博客,下载:GitHub - tracyone/program_font: fonts for programmer 然后运行&#…...
String类-equals和==的区别-遍历-SubString()-StringBuilder-StringJoiner-打乱字符串
概述 String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候…...
IDEA的LeetCode插件的设置
一、下载插件 选择点击File->Setting->Plugins:搜索LeetCode 二、打开这个插件 选择View —>Tool Windows—>leetcode 三、登陆自己的账号 关于下面几个参数的定义,官方给的是: Custom code template: 开启使用自定义模板&…...
2024.2.29 模拟实现 RabbitMQ —— 项目展示
目录 项目介绍 核心功能 核心技术 演示直接交换机 演示扇出交换机 演示主题交换机 项目介绍 此处我们模拟 RabbitMQ 实现了一个消息队列服务器 核心功能 提供了 虚拟主机、交换机、队列、绑定、消息 概念的管理九大核心 API 创建队列、销毁队列、创建交换机、销毁交换机、…...
React htmlfor
注意,在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。 在React中,当我们需要为一个表单元素设置标签时,可以使用htmlFor属性。它…...
现代化数据架构升级:毫末智行自动驾驶如何应对年增20PB的数据规模挑战?
毫末智行是一家致力于自动驾驶的人工智能技术公司,其前身是长城汽车智能驾驶前瞻分部,以零事故、零拥堵、自由出行和高效物流为目标,助力合作伙伴重塑和全面升级整个社会的出行及物流方式。 在自动驾驶领域中,是什么原因让毫末智行…...
理解Stable Diffusion、LoRA、Dreambooth、Hypernetworks、Textual Inversion、Checkpoint
前言 在深度学习和人工智能的领域中,模型生成和调整技术的快速发展为创造性内容的自动化提供了新的可能性。本文将介绍四种重要的模型技术——Stable Diffusion、LoRA、Dreambooth、和Hypernetworks——它们在生成艺术、个性化模型调整和网络结构设计方面各自的特点…...
spring boot3登录开发-2(1图形验证码接口实现)
⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途。 目录 前置条件 内容简介 图形验证码接口实现 导入糊涂工具依赖 接口分析 编写验证码接口 测试验证码接口 前置条件 …...
网络编程中的问题总结
1、服务端重启后bind失败,因为TCP 套接字状态 TIME_WAIT 引起,该状态在套接字关闭后约保留 2 到 4 分钟。在 TIME_WAIT 状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。可以通过setsockopt()设置Socket描述符的选项S…...
数据结构-关键路径
介绍 在AOV网的基础上,如果用对应边来表示活动持续时间,这种有向图被称为AOE网在AOE网中,入度为0的为源点,出度为0的为汇点,整张网看做是一件事情完成的过程,那么这两个点就是事情的开始和结束。每个活动持…...
进程间通信学习笔记(共享内存)
内存映射概念: 共享内存可以通过mmap()映射普通文件使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通文件一样对文件进行访问,不必再强调read,write。 mmap的优点: 实现了用户空间和内核空间的高效交互方式 mmap的…...
ChatGPT学习第三周
📖 学习目标 ChatGPT在各行各业的应用 探索ChatGPT在不同领域(如教育、客户服务等)的实际应用案例。 ChatGPT的局限性和挑战 讨论ChatGPT面临的挑战,包括偏见、误解及其限制。 ✍️ 学习活动 学习资料 《人工智能通用大模型(…...
R语言混合效应(多水平/层次/嵌套)模型及贝叶斯实现技术应用
回归分析是科学研究中十分重要的数据分析工具。随着现代统计技术发展,回归分析方法得到了极大改进。混合效应模型(Mixed effect model),即多水平模(Multilevel model)/分层模型(Hierarchical Model)/嵌套模…...
[C++]使用C++部署yolov9的tensorrt模型进行目标检测
部署YOLOv9的TensorRT模型进行目标检测是一个涉及多个步骤的过程,主要包括准备环境、模型转换、编写代码和模型推理。 首先,确保你的开发环境已安装了NVIDIA的TensorRT。TensorRT是一个用于高效推理的SDK,它能对TensorFlow、PyTorch等框架训…...
eureka注册中心做了哪些事情/原理?
1.服务注册: 将eureka client发送过来的元数据存储到注册表中 2.服务续约: eureka client默认会每30秒向eureka server发送一次心跳来进行服务续约,通过这一行动来表示自己没有出现故障; 3.服务…...
2026届毕业生推荐的五大AI论文工具实际效果
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 于学术写作流程里,恰当运用论文AI工具能够明显提高研究效率。当下主流的论文AI工…...
Windows系统下Java环境管理指南:如何让BurpSuite 2022.8.2与旧版Java项目和平共处?
Windows系统下Java多版本共存实战:BurpSuite 2022与老旧工具兼容指南 你是否遇到过这样的场景:刚装好BurpSuite 2022.8.2准备测试,突然发现手头的AWVS旧版扫描器无法启动了?或者SQLMap的图形化界面报错提示Java版本不兼容…...
FPGA上实现96.58%精度:三阶流水线CNN加速器Verilog设计避坑指南
FPGA上实现96.58%精度的三阶流水线CNN加速器设计实战 在边缘计算和实时图像处理领域,FPGA因其并行计算能力和低延迟特性成为CNN加速的理想平台。但将软件层面的神经网络模型高效映射到硬件电路,始终是工程师面临的核心挑战。本文将深入解析一种通过三阶流…...
Windows 11终极优化指南:使用Win11Debloat脚本免费提升系统性能40%
Windows 11终极优化指南:使用Win11Debloat脚本免费提升系统性能40% 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to decl…...
别再为WebSocket握手失败头疼了!手把手教你用Nginx 1.18+配置WSS反向代理(附SSL证书配置)
从零到一:Nginx反向代理WebSocket的终极避坑指南 凌晨三点,服务器监控突然告警——你的在线协作平台WebSocket连接全部断开。控制台里堆满了101 Switching Protocols错误,而本地测试时明明一切正常。这种场景对经历过生产环境WebSocket部署的…...
FlinkCDC实战:从单表到多源合并,一键搞定MySQL实时同步(Flink 1.16.2)
1. 环境准备与基础配置 在开始FlinkCDC实战之前,我们需要先搭建好基础环境。我建议使用Linux系统进行操作,这里以CentOS 7为例。首先确保你已经安装了JDK 1.8,这是Flink运行的基本要求。 下载Flink 1.16.2安装包时,要注意选择与Sc…...
轻松解包网易游戏资源:unnpk工具完全指南
轻松解包网易游戏资源:unnpk工具完全指南 【免费下载链接】unnpk 解包网易游戏NeoX引擎NPK文件,如阴阳师、魔法禁书目录。 项目地址: https://gitcode.com/gh_mirrors/un/unnpk 你是否曾好奇阴阳师、魔法禁书目录等网易游戏中的精美角色、场景和音…...
网工实战笔记:如何在企业级AP(如Aruba或Cisco)上配置和优化802.11ax的RU分配策略
企业级AP实战:802.11ax RU分配策略的配置与优化指南 当企业Wi-Fi网络从传统802.11ac升级到802.11ax(Wi-Fi 6)时,最关键的突破莫过于OFDMA技术和资源单元(RU)的动态分配能力。想象一下这样的场景:…...
3步掌握Scarab:空洞骑士模组管理的终极解决方案
3步掌握Scarab:空洞骑士模组管理的终极解决方案 【免费下载链接】Scarab An installer for Hollow Knight mods written in Avalonia. 项目地址: https://gitcode.com/gh_mirrors/sc/Scarab Scarab是一款基于Avalonia框架开发的跨平台模组管理器,…...
从天气预报到视频预测:ConvLSTM实战项目入门(附PyTorch完整代码)
从天气预报到视频预测:ConvLSTM实战项目入门(附PyTorch完整代码) 当我们需要预测未来几小时的降雨量,或是推断视频下一帧的画面时,传统方法往往捉襟见肘。ConvLSTM的出现,为这类时空序列预测问题提供了全新…...
