reactFiberLane

news2024/12/25 14:34:55

Lane (车道模型)

英文单词lane翻译成中文表示"车道, 航道"的意思, 所以很多文章都将Lanes模型称为车道模型

Lane模型的源码在ReactFiberLane.js, 源码中大量使用了位运算(有关位运算的讲解,

首先引入作者对Lane的解释(相应的 pr), 这里简单概括如下:

  1. Lane类型被定义为二进制变量, 利用了位掩码的特性, 在频繁运算的时候占用内存少, 计算速度快.
    1. LaneLanes就是单数和复数的关系, 代表单个任务的定义为Lane, 代表多个任务的定义为Lanes
  2. Lane是对于expirationTime的重构, 以前使用expirationTime表示的字段, 都改为了lane
  renderExpirationtime -> renderLanes
  update.expirationTime -> update.lane
  fiber.expirationTime -> fiber.lanes
  fiber.childExpirationTime -> fiber.childLanes
  root.firstPendingTime and root.lastPendingTime -> fiber.pendingLanes
  1. 使用Lanes模型相比expirationTime模型的优势:
  2. Lanes把任务优先级从批量任务中分离出来, 可以更方便的判断单个任务与批量任务的优先级是否重叠.
// 判断: 单task与batchTask的优先级是否重叠
//1. 通过expirationTime判断
const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
//2. 通过Lanes判断
const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;

// 当同时处理一组任务, 该组内有多个任务, 且每个任务的优先级不一致
// 1. 如果通过expirationTime判断. 需要维护一个范围(在Lane重构之前, 源码中就是这样比较的)
const isTaskIncludedInBatch =
  taskPriority <= highestPriorityInRange &&
  taskPriority >= lowestPriorityInRange;
//2. 通过Lanes判断
const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;

Lanes使用单个 32 位二进制变量即可代表多个不同的任务, 也就是说一个变量即可代表一个组(group), 如果要在一个 group 中分离出单个 task, 非常容易.

expirationTime模型设计之初, react 体系中还没有Suspense 异步渲染的概念. 现在有如下场景: 有 3 个任务, 其优先级 A > B > C, 正常来讲只需要按照优先级顺序执行就可以了. 但是现在情况变了: A 和 C 任务是CPU密集型, 而 B 是IO密集型(Suspense 会调用远程 api, 算是 IO 任务), 即 A(cpu) > B(IO) > C(cpu). 此时的需求需要将任务B从 group 中分离出来, 先处理 cpu 任务A和C.

// 从group中删除或增加task

//1. 通过expirationTime实现
// 0) 维护一个链表, 按照单个task的优先级顺序进行插入
// 1) 删除单个task(从链表中删除一个元素)
task.prev.next = task.next;
// 2) 增加单个task(需要对比当前task的优先级, 插入到链表正确的位置上)
let current = queue;
while (task.expirationTime >= current.expirationTime) {
  current = current.next;
}
task.next = current.next;
current.next = task;
// 3) 比较task是否在group中
const isTaskIncludedInBatch =
  taskPriority <= highestPriorityInRange &&
  taskPriority >= lowestPriorityInRange;

// 2. 通过Lanes实现
// 1) 删除单个task
batchOfTasks &= ~task;
// 2) 增加单个task
batchOfTasks |= task;
// 3) 比较task是否在group中
const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;

Lanes是一个不透明的类型, 只能在ReactFiberLane.js这个模块中维护. 如果要在其他文件中使用, 只能通过ReactFiberLane.js中提供的工具函数来使用.

分析车道模型的源码(ReactFiberLane.js中), 可以得到如下结论:

  1. 可以使用的比特位一共有 31 位
  2. 共定义了18 种车道(Lane/Lanes)变量, 每一个变量占有 1 个或多个比特位, 分别定义为LaneLanes类型.
  3. 每一种车道(Lane/Lanes)都有对应的优先级, 所以源码中定义了 18 种优先级(LanePriority).
  4. 占有低位比特位的Lane变量对应的优先级越高
    1. 最高优先级为SyncLanePriority对应的车道为SyncLane = 0b0000000000000000000000000000001.
    2. 最低优先级为OffscreenLanePriority对应的车道为OffscreenLane = 0b1000000000000000000000000000000.

位运算

什么是位运算? 程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。

比如

  • 0 在二进制中用 0 表示,我们用 0000 代表;
  • 1 在二进制中用 1 表示,我们用 0001 代表;

那么先看两个位元算符号 & 和 |:

  • & 对于每一个比特位,两个操作数都为 1 时, 结果为 1, 否则为 0
  • | 对于每一个比特位,两个操作数都为 0 时, 结果为 0, 否则为 1

我们看一下两个 1 & 0 和 1 | 0

如上 1 & 0 = 0 ,1 | 0 = 1

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

位运算的一个使用场景:

比如有一个场景下,会有很多状态常量 A,B,C…,这些状态在整个应用中在一些关键节点中做流程控制,比如:

if(value === A){ // TODO… }

如上判断 value 等于常量A ,那么进入到 if 的条件语句中。 此时是 value 属性是简单的一对一关系,但是实际场景下 value 可能是好几个枚举常量的集合,也就是一对多的关系,那么此时 value 可能同时代表 A 和 B 两个属性。如下图所示:

在这里插入图片描述

此时的问题就是如何用一个 value 表示 A 和 B 两个属性的集合。 这个时候位运算就派上用场了,因为可以把一些状态常量用 32 位的二进制来表示(这里也可以用其他进制),比如:

const A = 0b0000000000000000000000000000001
const B = 0b0000000000000000000000000000010
const C = 0b0000000000000000000000000000100

通过移位的方式让每一个常量都单独占一位,这样在判断一个属性是否包含常量的时候,可以根据当前位数的 1 和 0 来判断。

这样如果一个值即代表 A 又代表 B 那么就可以通过位运算的 | 来处理。就有

AB = A | B = 0b0000000000000000000000000000011

那么如果把 AB 的值赋予给 value ,那么此时的 value 就可以用来代表 A 和 B 。

此时当然不能直接通过等于或者恒等来判断 value 是否为 A 或者 B ,此时就可以通过 & 来判断。具体实现如下:

const A = 0b0000000000000000000000000000001
const B = 0b0000000000000000000000000000010
const C = 0b0000000000000000000000000000100
const N = 0b0000000000000000000000000000000
const value = A | B
console.log((value & A ) !== N ) // true
console.log((value & B ) !== N ) // true
console.log((value & C ) !== N ) // false

位运算在react中的运用

export const NoLanes = /*                        */ 0b0000000000000000000000000000000;
const SyncLane = /*                        */ 0b0000000000000000000000000000001;

const InputContinuousHydrationLane = /*    */ 0b0000000000000000000000000000010;
const InputContinuousLane = /*             */ 0b0000000000000000000000000000100;

const DefaultHydrationLane = /*            */ 0b0000000000000000000000000001000;
const DefaultLane = /*                     */ 0b0000000000000000000000000010000;

const TransitionHydrationLane = /*                */ 0b0000000000000000000000000100000;
const TransitionLane = /*                        */ 0b0000000000000000000000001000000;

如上 SyncLane 代表的数值是 1,它却是最高的优先级,也即是说 lane 的代表的数值越小,此次更新的优先级就越大 ,在新版本的 React 中,还有一个新特性,就是 render 阶段可能被中断,在这个期间会产生一个更高优先级的任务,那么会再次更新 lane 属性,这样多个更新就会合并,这样一个 lane 可能需要表现出多个更新优先级。

我们来看一下 React 是如何通过位运算分离出优先级的。

function getHighestPriorityLane(lanes) {
  return lanes & -lanes;
}

如上就是通过 lanes & -lanes 分离出最高优先级的任务的,我们来看一下具体的流程。

比如 SyncLane 和 InputContinuousLane 合并之后的任务优先级 lane 为

SyncLane = 0b0000000000000000000000000000001 InputContinuousLane = 0b0000000000000000000000000000100

lane = SyncLane | InputContinuousLane lane = 0b0000000000000000000000000000101

那么通过 lanes & -lanes 分离出 SyncLane。

首先我们看一下 -lanes,在二进制中需要用补码表示为:

-lane = 0b1111111111111111111111111111011

那么接下来执行 lanes & -lanes 看一下,& 的逻辑是如果两位都是 1 则设置改位为 1,否则为 0。

那么 lane & -lane ,只有一位(最后一位)全是 1,所有合并后的内容为:

lane & -lane = 0b0000000000000000000000000000001

可以看得出来 lane & -lane 的结果是 SyncLane,所以通过 lane & -lane 就能分离出最高优先级的任务。

const SyncLane = 0b0000000000000000000000000000001
const InputContinuousLane = 0b0000000000000000000000000000100
const lane = SyncLane | InputContinuousLane
console.log( (lane & -lane) === SyncLane  ) // true

优先级区别和联系

在源码中, 3 种优先级位于不同的 js 文件, 是相互独立的.

注意:

  • LanePrioritySchedulerPriority从命名上看, 它们代表的是优先级
  • ReactPriorityLevel从命名上看, 它代表的是等级而不是优先级, 它用于衡量LanePrioritySchedulerPriority的等级.

LanePriority

LanePriority`: 属于`react-reconciler`包, 定义于`ReactFiberLane.js
export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;

const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;

// .....

const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;

fiber构造过程相关的优先级(如fiber.updateQueue,fiber.lanes)都使用LanePriority.

由于本节重点介绍优先级体系以及它们的转换关系, 关于Lane(车道模型)fiber树构造时的具体使用, 在fiber 树构造章节详细解读.

SchedulerPriority

SchedulerPriority, 属于scheduler包, 定义于SchedulerPriorities.js

export const NoPriority = 0;
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;

scheduler调度中心相关的优先级使用SchedulerPriority.

ReactPriorityLevel

reactPriorityLevel, 属于react-reconciler包, 定义于SchedulerWithReactIntegration.js

export const ImmediatePriority: ReactPriorityLevel = 99;
export const UserBlockingPriority: ReactPriorityLevel = 98;
export const NormalPriority: ReactPriorityLevel = 97;
export const LowPriority: ReactPriorityLevel = 96;
export const IdlePriority: ReactPriorityLevel = 95;
// NoPriority is the absence of priority. Also React-only.
export const NoPriority: ReactPriorityLevel = 90;

LanePrioritySchedulerPriority通过ReactPriorityLevel进行转换

转换关系

为了能协同调度中心(scheduler包)和 fiber 树构造(react-reconciler包)中对优先级的使用, 则需要转换SchedulerPriorityLanePriority, 转换的桥梁正是ReactPriorityLevel.

SchedulerWithReactIntegration.js中, 可以互转SchedulerPriorityReactPriorityLevel:

// 把 SchedulerPriority 转换成 ReactPriorityLevel
export function getCurrentPriorityLevel(): ReactPriorityLevel {
  switch (Scheduler_getCurrentPriorityLevel()) {
    case Scheduler_ImmediatePriority:
      return ImmediatePriority;
    case Scheduler_UserBlockingPriority:
      return UserBlockingPriority;
    case Scheduler_NormalPriority:
      return NormalPriority;
    case Scheduler_LowPriority:
      return LowPriority;
    case Scheduler_IdlePriority:
      return IdlePriority;
    default:
      invariant(false, 'Unknown priority level.');
  }
}

// 把 ReactPriorityLevel 转换成 SchedulerPriority
function reactPriorityToSchedulerPriority(reactPriorityLevel) {
  switch (reactPriorityLevel) {
    case ImmediatePriority:
      return Scheduler_ImmediatePriority;
    case UserBlockingPriority:
      return Scheduler_UserBlockingPriority;
    case NormalPriority:
      return Scheduler_NormalPriority;
    case LowPriority:
      return Scheduler_LowPriority;
    case IdlePriority:
      return Scheduler_IdlePriority;
    default:
      invariant(false, 'Unknown priority level.');
  }
}

ReactFiberLane.js中, 可以互转LanePriorityReactPriorityLevel:

export function schedulerPriorityToLanePriority(
  schedulerPriorityLevel: ReactPriorityLevel,
): LanePriority {
  switch (schedulerPriorityLevel) {
    case ImmediateSchedulerPriority:
      return SyncLanePriority;
    // ... 省略部分代码
    default:
      return NoLanePriority;
  }
}

export function lanePriorityToSchedulerPriority(
  lanePriority: LanePriority,
): ReactPriorityLevel {
  switch (lanePriority) {
    case SyncLanePriority:
    case SyncBatchedLanePriority:
      return ImmediateSchedulerPriority;
    // ... 省略部分代码
    default:
      invariant(
        false,
        'Invalid update priority: %s. This is a bug in React.',
        lanePriority,
      );
  }
}

优先级使用

通过reconciler运行流程中的归纳, reconciler从输入到输出一共经历了 4 个阶段, 在每个阶段中都会涉及到与优先级相关的处理. 正是通过优先级的灵活运用, React实现了可中断渲染,时间切片(time slicing),异步渲染(suspense)等特性.

突然很后悔当初没有跟老师好好学数电,在此缅怀许院王教授

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

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

相关文章

T10打卡-学习笔记

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 配置环境 import matplotlib.pyplot as plt import numpy as np #隐藏警告 import warnings warnings.filterwarnings(ignore)from tensorflow.keras import …

RCE漏洞及绕过

目录 1、RCE概述 &#xff08;1&#xff09;命令执行函数 &#xff08;2&#xff09;代码执行函数 2、回调后门 3、eval和assert 限制字符长度绕过 &#xff08;1&#xff09;反引号或exec &#xff08;2&#xff09;file_put_contents写入文件 &#xff08;3&#xff…

数据结构——循环队列

目录 循环队列的基本知识 循环队列的实现 定义 各个接口的实现 循环队列的基本知识 循环队列的定义 循环队列&#xff08;Circular Queue&#xff09;是一种使用固定大小的数组实现的队列&#xff0c;它将数组的首尾相连&#xff0c;形成环形&#xff0c;以充分利用空间并实…

react组件优化之React.PureComponent,React.memo

在开发中我们经常会考虑项目的优化问题&#xff0c;react作为现在前端的热门框架用的人肯定是非常的多。项目的优化问题也是非常重要的一部分&#xff0c;能大大提高项目的流畅度&#xff0c;用户体验会非常好。react项目中会用到大量的组件嵌套&#xff0c;减少一些组件的不必…

第二证券:虚拟现实概念强势,博士眼镜三连板,星星科技涨停

虚拟现实概念14日盘中再度走强&#xff0c;到发稿&#xff0c;硕贝德、博士眼镜、星星科技“20cm”涨停&#xff0c;亚世光电、亿道信息、卓翼科技亦涨停&#xff0c;佳禾智能涨超9%。 值得注意的是&#xff0c;博士眼镜已连续3个交易日涨停。公司昨日在出资者互动途径表示&am…

【二叉树进阶】--- 根据二叉树创建字符串

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 数据结构 从本篇文章开始&#xff0c;博主将分享一些结合二叉树的进阶算法题。 &#x1f3e0; 根据二叉树创建字符串 &#x1f4cc; 题目内容 根据二叉…

若依框架中的mybatis依赖在哪里?

对于刚刚接触若依框架的朋友,可能会比较懵逼,因为他可能在依赖文件中没有找到mybatis的依赖是在什么地方引入的,所以本章教程,就告诉你这个依赖是在什么地方引入的。 在ruoyi-common模块中的pom.xml 存在一个pagehelper-spring-boot-starter <!-- pagehelper 分页插件 -…

一文HDMI (High-Definition Multimedia Interface)

HDMI&#xff08;High-Definition Multimedia Interface&#xff0c;高清多媒体接口&#xff09;是一种紧凑的音视频接口&#xff0c;它能够将未压缩的视频数据以及压缩或未压缩的数字音频数据&#xff0c;从符合HDMI标准的源设备无缝传输到兼容的计算机显示器、视频投影仪、数…

springboot学生练习自测系统-计算机毕业设计源码48462

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2 学生练习自测系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.4 系统用例分析 2.5本章小…

STM32学习笔记13-FLASH闪存

FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口&#xff08;外设&#xff09;可以对程序存储器和选项字节进行擦除和编程读写FLASH的用途&#xff1a; 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 通过在…

【瑞芯微RV1126(板端摄像头图像数据采集)】②使用v4l2视频设备驱动框架采集图像数据,按键拍照并显示

RV1126开发板&#xff1a;使用v4l2视频设备驱动框架采集图像数据 前言一、按键二、LCD显示三、V4L2 摄像头应用编程四、完整代码 前言 本系列的目的是&#xff0c;不仅仅将能够进行图片推理的模型部署于板端&#xff0c;还提供了两种摄像头数据采集的方法&#xff0c;集成到自…

【python】OpenCV—Optical Flow

文章目录 1、光流2、Opencv 中光流的实现3、稀疏光流4、密集光流4.1、farneback4.2、lucaskanade_dense4.3、rlof 5、涉及到的库5.1、cv2.goodFeaturesToTrack5.2、cv2.calcOpticalFlowPyrLK5.3、cv2.optflow.calcOpticalFlowSparseToDense5.4、cv2.calcOpticalFlowFarneback5.…

超赞!墙裂推荐这款开箱即用、永久免费的运维监控平台

文章目录 简介一、初次印象&#xff1a;直观而强大的界面二、深入体验&#xff1a;6个重点功能模块1、告警管理2、综合监控3、业务服务4、网络拓扑5、可视化管理6、知识库7、报表管理 Lerwee AI 在当今这个数字化时代&#xff0c;企业依赖于强大的IT基础设施来支持其日常运营。…

微软Detours Hook库编译与使用

Detours 是微软开发的一个强大的Windows API钩子库&#xff0c;用于监视和拦截函数调用。它广泛应用于微软产品团队和众多独立软件开发中&#xff0c;旨在无需修改原始代码的情况下实现函数拦截和修改。Detours 在调试、监控、日志记录和性能分析等方面表现出色&#xff0c;已成…

java之如何爬取本地数据(利用正则表达式)

public class RegexDemo4 {public static void main(String[] args) {String s"程序员学习java&#xff0c;""电话&#xff1a;181512516758&#xff0c;18512508907" "或者联系邮箱&#xff1a;boniuitcast.cn&#xff0c;""座机电话&…

基于vue框架的RTY个人记账管理系统03jc1(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,收入分类,支出分类,收入信息,支出信息,开支预算,负债信息 开题报告内容 基于Vue框架的RTY个人记账管理系统 开题报告 一、研究背景与意义 随着社会经济的快速发展和人们生活水平的不断提升&#xff0c;个人财务管理成为越来越…

centos7.9 内核升级至5.4

一、修改yum源 查看现有系统内核版本&#xff1a; 备份系统自带的yum源&#xff1a; 一、修改CentOS-Base.repo ## cat CentOS-Base.repo # CentOS-Base.repo # # The mirror system uses the connecting IP address of the client and the # update status of each mirror t…

详细解读keepalived高可用集群

一.高可用集群 1.1 集群类型 LB&#xff1a;Load Balance 负载均衡LVS/HAProxy/nginx&#xff08;http/upstream, stream/upstream&#xff09;HA&#xff1a;High Availability 高可用集群数据库、RedisSPoF: Single Point of Failure&#xff0c;解决单点故障HPC&#xff1…

docker容器挂载USB串口设备

1、在容器所在宿主机确认USB串口设备 有两种方法可以将USB设备挂载到容器中: 使用--privileged参数或者使用--device参数 --prvleged参数可以让容器拥有主机的所有特权&#xff0c;包括所有可以访问USB设备。--device参数可以针对特定的设备挂载到容器中。 [rootdocker40 ~]…

【经验总结】ShardingSphere+Springboot-01模式参数配置

文章目录 详细配置&#xff08;boot&#xff09;一、模式配置&数据源配置1.1 模式配置1.2 数据源配置1.3 默认数据源的配置默认数据源配置结论 二、基础属性配置2.1 在日志中打印 SQL2.2 在程序启动和更新时&#xff0c;是否检查分片元数据的结构一致性 详细配置&#xff0…