- Published on
位运算
- Authors

- Name
- 李丹秋
React中常见的位运算方法 (05-31)
// 检测两个位标志之间是否有相同的标志,大部分的lane(非合成lane)只占据一个bit位,所以除非两个lane完全相同,否则不会有相同的标志
export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) {
return (a & b) !== NoLanes;
}
React中的优先级
位运算
| 运算符 | 用法 | 描述 |
|---|---|---|
| 与 & | a & b | 位与运算符,两个操作数中对应的位都是1时,结果才为1,否则为0 |
| 非 ~ | ~a | 按位取反运算符,对数据的每个二进制位取反,即把1变为0,把0变为1 |
| 或 | | a | b | 位或运算符,两个二进制位中只要有一个为1,结果就是1,否则为0 |
| 异或 ^ | a ^ b | 异或运算符,两个操作数的位中,相同则结果为0,不同则结果为1 |
| 右移 >> | a >> b | 右移运算符,将一个数的二进制位全部右移若干位,保持符号位不变,高位补0 |
| 无符号右移 >>> | a >>> b | 无符号右移运算符,将一个数的二进制位全部右移若干位,高位补0 |
| 取反 ^ | a ^ b | 异或运算符,两个操作数的位中,相同则结果为0,不同则结果为1 |
| 与赋值 &= | a &= b | 与赋值运算符,与运算后赋值给左操作数 |
| 或赋值 |= | a |= b | 或赋值运算符,或运算后赋值给左操作数 |
| 异或赋值 ^= | a ^= b | 异或赋值运算符,异或运算后赋值给左操作数 |
| 右移赋值 >>= | a >>= b | 右移赋值运算符,右移后赋值给左操作数 |
| 无符号右移赋值 >>>= | a >>>= b | 无符号右移赋值运算符,无符号右移后赋值给左操作数 |
更新优先级
React 中是存在不同优先级的任务的,比如用户文本框输入内容,需要 input 表单控件,如果控件是受控的(受数据驱动更新视图的模式),也就是当我们输入内容的时候,需要改变 state 触发更新,在把内容实时呈现到用户的界面上,这个更新任务就比较高优先级的任务。
相比表单输入的场景,比如一个页面从一个状态过渡到另外一个状态,或者一个列表内容的呈现,这些视觉的展现,并不要求太强时效性,期间还可能涉及到与服务端的数据交互,所以这个更新,相比于表单输入,就是一个低优先级的更新。
如果一个用户交互中,仅仅出现一个更新任务,那么 React 只需要公平对待这些更新就可以了。 但是问题是可能存在多个更新任务,举一个例子:远程搜索功能,当用户输入内容,触发列表内容的变化,这个时候如果把输入表单和列表更新放在同一个优先级,无论在 js 执行还是浏览器绘制,列表更新需要的时间远大于一个输入框更新的时间,所以输入框频繁改变内容,会造成列表频繁更新,列表的更新会阻塞到表单内容的呈现,这样就造成了用户不能及时看到输入的内容,造成了一个很差的用户体验。
所以 React 解决方案就是多个更新优先级的任务存在的时候,高优先级的任务会优先执行,等到执行完高优先级的任务,在回过头来执行低优先级的任务,这样保证了良好的用户体验。这样就解释了为什么会存在不同优先级的任务,那么 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;
function getHighestPriorityLanes(lanes) {
/* 通过 getHighestPriorityLane 分离出优先级高的任务 */
switch (getHighestPriorityLane(lanes)) {
case SyncLane:
return SyncLane;
case InputContinuousHydrationLane:
return InputContinuousHydrationLane;
...
}
function getHighestPriorityLane(lanes) {
return lanes & -lanes;
}
const SyncLane = 0b0000000000000000000000000000001
const InputContinuousLane = 0b0000000000000000000000000000100
const lane = SyncLane | InputContinuousLane
console.log( (lane & -lane) === SyncLane ) // true
更新标识flag
export const NoFlags = /* */ 0b00000000000000000000000000;
export const PerformedWork = /* */ 0b00000000000000000000000001;
export const Placement = /* */ 0b00000000000000000000000010;
export const Update = /* */ 0b00000000000000000000000100;
export const Deletion = /* */ 0b00000000000000000000001000;
export const ChildDeletion = /* */ 0b00000000000000000000010000;
export const ContentReset = /* */ 0b00000000000000000000100000;
export const Callback = /* */ 0b00000000000000000001000000;
export const DidCapture = /* */ 0b00000000000000000010000000;
export const ForceClientRender = /* */ 0b00000000000000000100000000;
export const Ref = /* */ 0b00000000000000001000000000;
export const Snapshot = /* */ 0b00000000000000010000000000;
export const Passive = /* */ 0b00000000000000100000000000;
export const Hydrating = /* */ 0b00000000000001000000000000;
export const Visibility = /* */ 0b00000000000010000000000000;
export const StoreConsistency = /* */ 0b00000000000100000000000000;
比如一些小朋友在做一个寻宝的游戏,在沙滩中埋了很多宝藏,有专门搜索这些宝藏的仪器,也有挖这些宝藏的工具,那么小朋友中会分成两组,一组负责拿仪器寻宝,另外一组负责挖宝,寻宝的小朋友在前面,找到宝藏之后不去直接挖,而是插上小旗子 (flags) 证明这个地方有宝藏,接下来挖宝的小朋友统一拿工具挖宝。这个流程非常高效,把不同的任务分配给不同的小朋友,各尽其职。
React 的更新流程和如上这个游戏如出一撤,也是分了两个阶段,第一个阶段就像寻宝的小朋友一样,找到待更新的地方,设置更新标志 flags,接下来在另一个阶段,通过 flags 来证明当前 fiber 发生了什么类型的更新,然后执行这些更新。
const NoFlags = 0b00000000000000000000000000;
const PerformedWork =0b00000000000000000000000001;
const Placement = 0b00000000000000000000000010;
const Update = 0b00000000000000000000000100;
//初始化
let flag = NoFlags
//发现更新,打更新标志
flag = flag | PerformedWork | Update
//判断是否有 PerformedWork 种类的更新
if(flag & PerformedWork){
//执行
console.log('执行 PerformedWork')
}
//判断是否有 Update 种类的更新
if(flag & Update){
//执行
console.log('执行 Update')
}
if(flag & Placement){
//不执行
console.log('执行 Placement')
}
如上会打印 执行 PerformedWork ,上面的流程清晰的描述了在 React 打更新标志,又如何判断更新类型的。