php求17的二进制,react17源码中部分二进制计算的解释

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

js中的二进制位运算都是以32位补码的形式计算的,更多解释可以参考mdn

1.关于上下文的切换

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

varNoContext=

/* */

0;

varBatchedContext=

/* */

1;

varEventContext=

/* */

2;

varDiscreteEventContext=

/* */

4;

varLegacyUnbatchedContext=

/* */

8;

varRenderContext=

/* */

16;

varCommitContext=

/* */

32;

varRetryAfterError=

/* */

64;

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

NoContext0000000

BatchedContext0000001

EventContext0000010

DiscreteEventContext0000100

LegacyUnbatchedContext0001000

RenderContext0010000

CommitContext0100000

RetryAfterError1000000

实际参与计算时应该是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)){

{

throwError("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如下(二进制形式):

exportconstNoLanes:Lanes=/* */0b0000000000000000000000000000000;

exportconstNoLane:Lane=/* */0b0000000000000000000000000000000;

exportconstSyncLane:Lane=/* */0b0000000000000000000000000000001;

exportconstSyncBatchedLane:Lane=/* */0b0000000000000000000000000000010;

exportconstInputDiscreteHydrationLane:Lane=/* */0b0000000000000000000000000000100;

constInputDiscreteLanes:Lanes=/* */0b0000000000000000000000000011000;

constInputContinuousHydrationLane:Lane=/* */0b0000000000000000000000000100000;

constInputContinuousLanes:Lanes=/* */0b0000000000000000000000011000000;

exportconstDefaultHydrationLane:Lane=/* */0b0000000000000000000000100000000;

exportconstDefaultLanes:Lanes=/* */0b0000000000000000000111000000000;

constTransitionHydrationLane:Lane=/* */0b0000000000000000001000000000000;

constTransitionLanes:Lanes=/* */0b0000000001111111110000000000000;

constRetryLanes:Lanes=/* */0b0000011110000000000000000000000;

exportconstSomeRetryLane:Lanes=/* */0b0000010000000000000000000000000;

exportconstSelectiveHydrationLane:Lane=/* */0b0000100000000000000000000000000;

constNonIdleLanes=/* */0b0000111111111111111111111111111;

exportconstIdleHydrationLane:Lane=/* */0b0001000000000000000000000000000;

constIdleLanes:Lanes=/* */0b0110000000000000000000000000000;

exportconstOffscreenLane:Lane=/* */0b1000000000000000000000000000000;

关于lane的基本使用1. 创建fiber

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

2. 创建update

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

...

varlane=requestUpdateLane(fiber);

varupdate=createUpdate(eventTime,lane);

...

functionrequestUpdateLane(fiber){

...

varmode=fiber.mode;

if((mode&BlockingMode)===NoMode){

returnSyncLane;

}elseif((mode&ConcurrentMode)===NoMode){

returngetCurrentPriorityLevel()===ImmediatePriority$1?SyncLane:SyncBatchedLane;

}

...

}

mode一般来说有如下几种:

varNoMode=0;

varStrictMode=1;

varBlockingMode=2;

varConcurrentMode=4;

varProfileMode=8;

varDebugTracingMode=16;

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

if(tag===ConcurrentRoot){

mode=ConcurrentMode|BlockingMode|StrictMode;

}elseif(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中,会

更新fiber上的lanes字段:

sourceFiber.lanes=mergeLanes(sourceFiber.lanes,lane);

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

while(parent!==null){

...

parent.childLanes=mergeLanes(parent.childLanes,lane);

...

}

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

更新root根节点的各个字段

pendingLanes:root.pendingLanes|=updateLane;

suspendedLanes,pingedLanesvarhigherPriorityLanes=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长度的数组,每一位对应一个lanevareventTimes=root.eventTimes;

varindex=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的时间

未完待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值