react17源码中部分二进制计算的解释

这篇具有很好参考价值的文章主要介绍了react17源码中部分二进制计算的解释。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

theme: qklhk-chocolate
highlight: a11y-dark

react17放弃了之前的expirationTime而启用了lane模型,故而在原来16的基础上又产生了更多的二进制运算,在接下来的一段时间我打算把这些二进制运算都整明白了、

关于react为什么会启用lane模型的官方解释
js中的二进制位运算都是以32位补码的形式计算的,更多解释可以参考mdn

1.关于上下文的切换

在react的更新中,executionContext按照字面意思即为执行上下文,executionContext的默认值是0NoContext, 此后的executionContext的更新中,都是与其他不同的上下文以按位或的运算的方式进行更新的,react17里的不同上下文有如下8种:

var NoContext =
/*             */
0;
var BatchedContext =
/*               */
1;
var EventContext =
/*                 */
2;
var DiscreteEventContext =
/*         */
4;
var LegacyUnbatchedContext =
/*       */
8;
var RenderContext =
/*                */
16;
var CommitContext =
/*                */
32;
var RetryAfterError =
/*       */
64;

分析位运算可能写出他们的二进制形式更加清晰一点,由上到下依次为:

NoContext              0000000
BatchedContext         0000001
EventContext           0000010
DiscreteEventContext   0000100
LegacyUnbatchedContext 0001000
RenderContext          0010000
CommitContext          0100000
RetryAfterError        1000000

实际参与计算时应该是32位的但是这里取7位是因为2^6是64多余的0对于我的分析来说是没有意义的。
react17一共在11个地方对context进行了|=方式的更新,不同的更新方式对应了不同的context的变量,比方,batchedUpdates的更新为executionContext |= BatchedContext;,unbatchedUpdates中的更新则相应的为executionContext |= LegacyUnbatchedContext

这些更新在其他地方大同小异,基本都是|=方式进行更新, 但在unbatchedUpdates更新前,却进行了一次这个操作:executionContext &= ~BatchedContext,这里我们先不管这段代码具体是什么作用,我们暂且讨论一下这个二进制运算会产生何种效果。首先假设在某一状态时 executionContext 与 BatchedContext 发生了一次运算:

 executionContext | BatchedContext
 0000000          | 0000001
 executionContext	= 	0000001	   

那么 executionContext &= ~BatchedContext:

	executionContext & ~BatchedContext
	0000001          &  1111110
	executionContext =  0000000

可以看到executionContext &= ~BatchedContext的效果其实就是还原了上次executionContext |= BatchedContext;前的executionContext。这个其实很好理解,要知道所有的context他在以上的7位二进制中只占了其中的一位,那么无论之前的executionContext与其中那个context进行按位或|运算,其结果就是只是让executionContext的某个位为1,拿BatchedContext举例他只是使得executionContext右边的第一位为1,而在按位取反之后,除了自己所占的那个位为0其余都成了1,再与executionContext进行按位与&运算,则executionContext中其他位为1的(也就是说executionContext可能与其他上下文也一起进行了|)是不会变得,只有~BatchedContext对应的那一位现在是0,因此不管executionContext中这个位置的数字是0还是1其结果最后都为0,也就是上边说的executionContext还原到了与BatchedContext|=前的状态。这条准则换成其他任何一个context都是一样的。

2.关于上下文的判断

拿一个地方举例:

if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {
    {
      throw Error( "Should not already be working." );
    }
  } 

这个if里的表达式为真,即 左边的要不等于NoContext,左边的不等于NoContext则说明executionContext里属于RenderContext或CommitContext的那个位为1,而那个位为1就说明 executionContext肯定与其中之一发生了按位或运算,而发生按位或也就代表着某个地方的代码执行过了。以上。

小结: 其实以上这种二进制的运算,应该属于二进制掩码的应用,二进制的运算除了数学上的特征比方左移右移相当于乘以2除以2,其他方面更觉得像一种图形一样的运算,因为 & | 这两种运算是不会产生进位的,因此看源码的二进制运算更多的应该从类似图形变换的角度去理解每个二进制运算的含义,而不是数字之间的运算。

3.关于lane

关于lane有篇文章个人决得讲的特别好,推荐一看

关于lane的基本解释

react17中使用了31位二进制来表示lane的概念,其中31位中占一位的变量称作lane,占据多位的称为lanes,react17中全部lane如下(二进制形式):

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;

export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;

export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;

const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;

export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;

const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;

const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;

export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;

export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;

const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;

export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;

export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;
关于lane的基本使用
1. 创建fiber

每一个fiber创建的时候其lanes,childLanes字段都被初始化为NoLanes

2. 创建update

react中无论是初始的渲染,还是setstate或者由hooks派发出来的更新操作,都会调用createupdate方法创建一个update对象,不同之处是,对于更新时的update对象来说lane字段是什么,是由与之相关的fiber的mode字段决定的:

...
var lane = requestUpdateLane(fiber);
var update = createUpdate(eventTime, lane);
...
function requestUpdateLane(fiber) {
  ...
  
  var mode = fiber.mode;

  if ((mode & BlockingMode) === NoMode) {
    return SyncLane;
  } else if ((mode & ConcurrentMode) === NoMode) {
    return getCurrentPriorityLevel() === ImmediatePriority$1 ? SyncLane : SyncBatchedLane;
  } 
  ...
}

mode一般来说有如下几种:

var NoMode = 0;
var StrictMode = 1;

var BlockingMode = 2;
var ConcurrentMode = 4;
var ProfileMode = 8;
var DebugTracingMode = 16;

HostRootFiber(整个react应用的初始fiber节点)初始化的时候,目前来看其tagLegacyRoot,在createHostRootFiber方法中赋予其mode:

if (tag === ConcurrentRoot) {
  mode = ConcurrentMode | BlockingMode | StrictMode;
} else if (tag === BlockingRoot) {
  mode = BlockingMode | StrictMode;
} else {
  mode = NoMode;
}

由其tag可以知道HostRootFiber的mode即为noMode,之后在beginwork开始创建各个子节点的fiber时,其fiber的mode直接继承自父节点:

      var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);

因此 对于大部分fiber来说,在一次更新中由其派发的update的lane是SyncLane。

3.更新过程中对fiber上各个字段的更新

每个更新时都会自scheduleUpdateOnFiber始,而在scheduleUpdateOnFiber中,会

  1. 更新fiber上的lanes字段:
	sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);

然后沿fiber树向上遍历,更新每个父节点fiber的childLanes字段

while (parent !== null) {
    ...
    parent.childLanes = mergeLanes(parent.childLanes, lane);
    ...
}

其中mergeLanes 就是将两个变量进行按位或运算,产生新的lanes。 即由此可以看到,当前各种各样的更新的lane最终都会在根节点的childLanes字段上有体现。

  1. 更新root根节点的各个字段
  • pendingLanes:
root.pendingLanes |= updateLane;
  • suspendedLanes,pingedLanes
var higherPriorityLanes = updateLane - 1; // Turns 0b1000 into 0b0111
root.suspendedLanes &= higherPriorityLanes;
root.pingedLanes &= higherPriorityLanes;

几句解释: higherPriorityLanes - 1 ,比方代码中的注释Turns 0b1000 into 0b0111,他假如是一个lanes字段,那么他的值就是比当前updateLane的优先级更高的各个lane按位或之后的结果,因为结合前边各个lane的值可以看到,越靠近右边的1的位置的优先级越高, 至于suspendedLanes,pingedLanes的更新就是保留了比当前优先级更高的lane。

  • eventTimes
    这是一个31长度的数组,每一位对应一个lane
var eventTimes = root.eventTimes;
var index = laneToIndex(updateLane); // 获取当前lane在eventTimes的数组索引
// We can always overwrite an existing timestamp because we prefer the most
/ recent event, and we assume time is monotonically increasing.
eventTimes[index] = eventTime;//eventTime是创建当前update的时间

未完待续。。。文章来源地址https://www.toymoban.com/news/detail-710366.html

  • 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。

到了这里,关于react17源码中部分二进制计算的解释的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [云原生] 二进制安装K8S一部分

    目前Kubernetes最新版本是v1.25,但大部分公司一般不会使用最新版本。 目前公司使用比较多的:老版本是v1.15,因为v1.16改变了很多API接口版本,国内目前使用比较多的是v1.18、v1.20。  组件部署: mater节点 mater01 192.168.136.100 kube-apiserver kube-controller-manager kube-scheduler etcd        

    2024年02月22日
    浏览(39)
  • 第78讲:截取MySQL Binlog二进制日志中特定部分内容的技巧

    我们通过Binlog二进制日志恢复数据时,一般都会先用备份恢复全库的数据,然后再使用Binlog恢复备份中不存在的数据,因此再使用Binlog进行数据恢复时,并不是直接恢复整个Binlog日志中的数据,只是恢复Binlog中的部分数据。 根据特定的情况以及需求去恢复Binlog日志中的数据时

    2024年02月03日
    浏览(56)
  • [云原生案例2.1 ] Kubernetes的部署安装 【单master集群架构 ---- (二进制安装部署)】节点部分

    Minikube是一个工具,可以在本地快速运行一个单节点微型K8S,仅用于学习、预览K8S的一些特性使用。 Kubeadm也是一个工具,提供kubeadm init和kubeadm join,用于快速部署K8S集群,相对简单。 生产首选,从官方下载发行版的二进制包,手动部署每个组件和自签TLS证书,组成K8S集群,

    2024年02月05日
    浏览(61)
  • k8s的二进制部署(源码包部署)

    实验条件: 主机名 IP地址 组件 作用 master01 20.0.0.17 kube-apiserver、kube-controller-manager、kube-scheduler、etcd k8s部署 master02 20.0.0.27 kube-apiserver、kube-controller-manager、kube-scheduler node01 20.0.0.37 kubelet、kube-proxy、etcd node02 20.0.0.47 kubelet、kube-proxy、etcd nginx01 20.0.0.11 nginx、keepalived 负载均衡

    2024年02月04日
    浏览(59)
  • 哈工大 计算机系统 二进制炸弹实验报告

    实验报告 实 验(三) 题     目  Binary Bomb          二进制炸弹   专       业      计算机学院          学    号               班    级                学       生              指 导 教 师                实 验 地 点        实 验 日 期     

    2023年04月15日
    浏览(48)
  • Linux教程——Linux软件包(源码包和二进制包)

    Linux下的软件包众多,且几乎都是经 GPL 授权、免费开源(无偿公开源代码)的。这意味着如果你具备修改软件源代码的能力,只要你愿意,可以随意修改。 GPL,全称 General Public License,中文名称“通用性公开许可证”,简单理解 GPL 就是一个保护软件自由的一个协议,经 GP

    2024年02月11日
    浏览(47)
  • 【JavaScript数据结构与算法】字符串类(计算二进制子串)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力 n 年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目

    2024年02月05日
    浏览(47)
  • [ARM汇编]计算机原理与数制基础—1.1.2 二进制与十进制数制转换

    在计算机中,我们通常使用二进制数制来表示数据,因为计算机的基本电平只有两种状态:高电平(通常表示为 1)和低电平(通常表示为 0)。而在我们的日常生活中,我们习惯使用十进制数制。为了方便理解,我们需要掌握二进制与十进制之间的转换方法。 二进制转十进

    2024年02月08日
    浏览(52)
  • [ARM汇编]计算机原理与数制基础—1.1.3 二进制补码

    在计算机中,为了表示有符号整数(即正数和负数),通常采用二进制补码表示法。二进制补码不仅可以表示负数,还能简化计算机的加法和减法运算。接下来,我们将介绍二进制补码的概念及其计算方法。 原码、反码和补码 在讨论补码之前,我们先了解一下原码和反码的

    2024年02月08日
    浏览(53)
  • MATLAB|基于改进二进制粒子群算法的含需求响应机组组合问题研究(含文献和源码)

    目录 主要内容      模型研究    1.改进二进制粒子群算法(BPSO) 2.模型分析   结果一览    下载链接 该程序复现《A Modified Binary PSO to solve the Thermal Unit Commitment Problem》,主要做的是一个考虑需求响应的机组组合问题,首先构建了机组组合问题的基本模型,在此基础上

    2024年02月19日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包