_版本:v18.2.0
本文为我花了大半年的时间潜心研究所写,转载请注明出处,谢谢
本文假设你读了本系列中的《触发onClick,react会执行哪些操作》、《react中suspense组件是如何重新渲染的》,基于此进行本文的分析研究
react中useTransition的执行原理
本例代码
useTransition在我看来是专门为Suspense组件而设计的,从源码层面可以看出其实现原理和Suspense组件或多或少有所关联
useTransition是将react的lane优先级机制从底层搬到api层面提供给用户使用的先例,在以前用户不需要关注react底层lane模型的运行机制,但是现在你需要了,哈哈
useTransition其实是使用较低优先级的lane去调度update,在startTransition函数中调用的所有hook都会生成一个较低优先级lane的update对象,在高优先级update未执行完成前是不会执行的
以此为示例解释useTransition是如何工作的,通过onClick事件触发setFlag
const [flag, setFlag] = useState('a')
const [isPending, startTransition] = useTransition()
<Suspense fallback={<div>Loading...</div>}>
{ flag === 'a' ? <Test1 /> : <Test2 /> }
</Suspense>
<button onClick={() => {
startTransition(() => {
setFlag('b')
});
}}>切换</button>
概述
1. 当本例的函数组件进行初次渲染的时候,会执行useState和useTransition,构建初始的hook链表 hook_flag -> hook_pending -> hook_transition
2. 用户触发onClick事件,执行startTransition函数
** 在此函数中首先会触发hook执行setPending(true),从而调用dispatchSetState去创建一个update1对象,此时的lane为DiscreteEventPriority,将此对象添加到hook_pending中,随后执行ensureRootIsScheduled函数调度此对象
** 由于update1的lane为DiscreteEventPriority,所以直接调度一个微任务去执行flushSyncCallbacks
** 先为ReactCurrentBatchConfig$2.transition赋值,然后执行setPending(false)再次调用dispatchSetState去创建一个update2对象
由于此时ReactCurrentBatchConfig$2.transition不为空,得到的lane为TransitionLane1,将此对象添加到hook_pending中,并且复用之前的调度
** 执行startTransition中的回调函数,在本例中就是执行setFlag('b'),调用dispatchSetState去创建一个update3对象
此时的lane为update2对象的lane,也就是TransitionLane1,将此对象添加到hook_flag中,并且复用之前的调度
** 经过以上步骤处理后
此时的hook链表为
hook_flag -> hook_pending
| |
|-->update3 |-->update1->update2
此时的root.pendingLanes为DiscreteEventPriority | TransitionLane1
3. 执行步骤2调度的微任务,触发flushSyncCallback函数,首先从root.pendingLanes中分离出优先级更高的lane,也就是DiscreteEventPriority优先处理,随后执行renderRootSync遍历workInProgress树
** 当遍历workInProgress树下探到本例的函数组件时,执行updateFunctionComponent会引起此函数组件重新执行,从而更新hook
首先处理useState('a'),调用updateState去执行updateReducer,获取update3.lane,此时为TransitionLane1,优先级不够需要跳过,重新构建baseQueue链表
然后处理useTransition(),调用updateTransition,执行updateState,处理hook_pending中存储的update链表,由于update1.lane为DiscreteEventPriority,优先级足够不需要跳过直接更新状态值即可,但是update2需要跳过
所以经过这步操作后,只有isPending变为true。协调此函数组件的孩子节点后,后续的下探处理逻辑和commit阶段的所有操作都是无用的,因为本例并没使用isPending
4. 执行完步骤3的commit阶段后,会再度执行ensureRootIsScheduled调度剩下的update,此时root.pendingLanes为TransitionLane1,本次调度的lane直接就是TransitionLane1
** 由于TransitionLane1的优先级比DiscreteEventPriority的低,所以需要先根据TransitionLane1获取其对应的事件优先级DefaultEventPriority,然后再根据NormalPriority去获取调度优先级NormalPriority
** 调度一个NormalPriority的callback在宏任务中执行performConcurrentWorkOnRoot
5. 执行步骤4调度的宏任务,执行performConcurrentWorkOnRoot,最终还是执行renderRootSync函数遍历workInProgress树
** 下探到本函数组件后,执行updateFunctionComponent,更新hook
首先处理useState('a'),由于update3.lane为TransitionLane1,优先级足够直接更新状态值即可
然后处理useTransition(),由于update2.lane为TransitionLane1,优先级足够直接更新状态值即可
经过这步处理后,所有hook的update链表都处理完毕了,isPending变为false,flag变为'b',Suspense组件中的动态组件需要替换成<Test2 />
** 在后续的下探过程中,遇到Offscreen节点会去执行动态import去加载新的动态组件,详细步骤在本系列的《react中suspense组件是如何重新渲染的》中
** 在回溯过程中,遇到Suspense节点,会将“退出状态”置为RootSuspendedWithDelay
** 在回溯执行完毕后,会判断“退出状态”如果为RootSuspendedWithDelay,会继续判断当前处理的lane是否为Transition类型的,本例这里肯定是
如果是,则直接结束执行,不会进入commit阶段,所产生的效果就是Suspense中的fallback不会渲染到页面,只会展示旧的动态组件
6. 后续的逻辑就是处理加载动态组件<Test2 />的promise的then语句中的代码,得到Module对象,再次调度一个NormalPriority优先级的宏任务,去渲染<Test2 />
初次执行函数组件中的hook
全局变量currentlyRenderingFiber$1存储当前workInProgress节点
useState -> mountState
1. hook = mountWorkInProgressHook()
创建hook对象并且将其插入hook链表中,hook链表保存在fiber节点的memoizedState属性上
2. 如果useState传入的参数是函数则执行,得到返回值initialState
3. hook.memoizedState = hook.baseState = initialState 保存初始值
4. 创建queue对象
queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState 旧值
}
注:每一个hook都会有一个queue对象,每一次此hook调用都会创建一个update对象并且串联成update链表,此链表存在queue对象的interleaved属性上
5. hook.queue = queue
6. dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue)
7. 返回 [hook.memoizedState, dispatch]
useTransition函数 -> mountTransition
1. const [isPending, setPending] = mountState(false)
从这里可以看出,调用useTransition实际上在hook链表中插入了两个节点,一个是useState的hook一个是useTransition的hook
2. start = startTransition.bind(null, setPending)
3. hook = mountWorkInProgressHook() 创建一个hook对象,并插入hook链表
4. hook.memoizedState = start
5. 返回 [isPending, start]
经过初次渲染后,hook链表大致如为 hook_flag -> hook_pending -> hook_transition
触发onClick事件执行startTransition
startTransition函数
1. 对比全局变量currentUpdatePriority和ContinuousEventPriority中较小的值,并且重新赋值给currentUpdatePriority
从这一步就可以看出,startTransition触发的优先级比ContinuousEventPriority要高
本例由onClick触发,所以这里优先级为DiscreteEventPriority
2. setPending(true) 调度一个微任务去执行flushSyncCallbacks
3. ReactCurrentBatchConfig$2.transition = { _updatedFibers = new Set() }
4. setPending(false)复用上一个调度
5. 执行startTransition传入的回调函数,也就是调用setFlag
6. transition._updatedFibers.clear()
触发hook
触发hook的调用链为
onClick
startTransition
1. setPending(true) -> 调用dispatchSetState处理hook_pending
2. setPending(false) -> 调用dispatchSetState处理hook_pending
3. setFlag('b') -> 调用dispatchSetState处理hook_flag
*1* setPending(true)
dispatchSetState(fiber, hook_pending.queue, action) 这里的fiber是currentlyRenderingFiber$1,action为true
1. lane = requestUpdateLane(fiber) 获取优先级,这里肯定为DiscreteEventPriority
2. 创建update对象
update = {
lane: lane,
action: action,
hasEagerState: false,
eagerState: null,
next: null
}
3. alternate = fiber.alternate
判断 fiber.lanes为NoLanes && (alternate为空 || alternate.lanes为NoLanes) 如果为true
刚执行第一个hook这里肯定为true,计算hook的初始值
1. lastRenderedReducer = queue.lastRenderedReducer 此为basicStateReducer函数
2. currentState = queue.lastRenderedState 旧值
3. eagerState = lastRenderedReducer(currentState, action) 得到新值true
触发basicStateReducer函数获取新值,typeof action === 'function' ? action(state) : action
4. update.hasEagerState = true
5. update.eagerState = eagerState
6. 通过Object.is比较eagerState和currentState是否相同,这里肯定不同
4. 执行enqueueConcurrentHookUpdate将update对象插入环形链表中,并且将本次update的lane合并到当前节点的lanes属性以及其祖先节点的childLanes属性
5. 根据lane优先级,去执行scheduleUpdateOnFiber调度此update对象
6. 执行entangleTransitionUpdate,此时内部逻辑未执行
*2* setPending(false)
dispatchSetState(fiber, hook_pending.queue, action) 这里的fiber是currentlyRenderingFiber$1,action为false
1. lane = requestUpdateLane(fiber) 获取优先级,这里处理后得到为TransitionLane1
2. 同上
3. 未执行
4. 同上
5. 同上,直接复用了当前的调度callback
6. 执行entangleTransitionUpdate,将lane添加到root.entanglements数组中,并且将lane合并到root.entangledLanes
*3* setFlag('b')
dispatchSetState(fiber, hook_flag.queue, action) 这里的fiber是currentlyRenderingFiber$1,action为'b'
1. lane = requestUpdateLane(fiber) 获取优先级,这里处理后得到全局变量currentEventTransitionLane,其存储步骤*2*的lane,所以也是TransitionLane1
2. 同上
3. 未执行
4. 同上
5. 同上,直接复用了当前的调度callback
6. 同上
调度update
scheduleUpdateOnFiber函数
1. root.pendingLanes |= lane
2. root.suspendedLanes = NoLanes
3. root.pingedLanes = NoLanes
4. 执行ensureRootIsScheduled
ensureRootIsScheduled函数
1. existingCallbackNode = root.callbackNode
2. 将“饥饿(已过期)”的lane添加到 expiredLanes 中
3. 调用getNextLanes获取nextLanes
4. newCallbackPriority = getHighestPriorityLane(nextLanes) 分离出优先级最高的lane
5. 比较root.callbackPriority和newCallbackPriority
1. 如果相同,表明当前已经有相同lane的调度callback,直接复用即可,结束此函数后续执行
2. 如果不同
如果root.callbackNode不为空,表明当前有低优先级的调度callback正在执行,需要将其取消掉
root.callbackNode.callback = null
6. 如果newCallbackPriority为SyncLane,此lane优先级最高
1. 将performSyncWorkOnRoot.bind(null, root)放入syncQueue数组中
2. 执行scheduleMicrotask(function () { flushSyncCallbacks() })调度一个微任务去执行flushSyncCallbacks
3. newCallbackNode = null
7. 如果newCallbackPriority不为SyncLane,此lane为低优先级的,本例中TransitionLane1走这里
1. 执行lanesToEventPriority获取lanes对应的事件优先级(本例为DefaultEventPriority),再根据此事件优先级获取对应的调度优先级 (本例为NormalPriority)
2. 根据调度优先级,执行scheduleCallback调度一个callback,入宏任务队列
newCallbackNode = scheduleCallback(NormalPriority, performConcurrentWorkOnRoot.bind(null, root))
8. root.callbackPriority = newCallbackPriority
9. root.callbackNode = newCallbackNode
getNextLanes函数
1. pendingLanes = root.pendingLanes
2. suspendedLanes = root.suspendedLanes
3. nonIdlePendingLanes = pendingLanes & NonIdleLanes
4. 如果nonIdlePendingLanes不为NoLanes
1. nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes 移除suspendedLanes
2. 如果nonIdleUnblockedLanes不为NoLanes
走到这一步就可以看出,是分离出suspendedLanes,优先处理剩下的lane
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes)
在getHighestPriorityLanes中对Transition类型的lane返回nonIdleUnblockedLanes & TransitionLanes,其他情况直接返回nonIdleUnblockedLanes
5. 如果root.entangledLanes
** 为NoLanes,返回nextLanes
** 不为NoLanes
1. entanglements = root.entanglements
2. lanes = nextLanes & root.entangledLanes
3. while (lanes > 0)
1. index = pickArbitraryLaneIndex(lanes) 获取lanes的最高位索引
2. lane = 1 << index 获取lanes最高位的二进制数,其余低位全部置零
3. nextLanes |= entanglements[index]
4. lanes &= ~lane 最高位过滤,继续遍历
调度步骤
调度update1
update1 = {
lane: DiscreteEventPriority,
action: true,
hasEagerState: true,
eagerState: null,
next: update2
}
root.pendingLanes |= DiscreteEventPriority
调度一个微任务去执行flushSyncCallbacks
调度update2
update2 = {
lane: TransitionLane1,
action: false,
hasEagerState: false,
eagerState: null,
next: update1
}
root.pendingLanes |= TransitionLane1
调度update3
update3 = {
lane: TransitionLane1,
action: 'b',
hasEagerState: false,
eagerState: null,
next: update3
}
root.pendingLanes |= TransitionLane1
对应的链表为
hook_flag -> hook_pending
| |
|-->update3 |-->update1->update2
触发微任务
执行flushSyncCallbacks函数,遍历syncQueue数组执行其中存储的函数,本例为performSyncWorkOnRoot
performSyncWorkOnRoot函数
1. lanes = getNextLanes(root, NoLanes) 从root.pendingLanes中分离出优先级更高的lane
由于此时的root.pendingLanes为 DiscreteEventPriority | TransitionLane1,所以分离出 DiscreteEventPriority
2. exitStatus = renderRootSync(root, lanes)
renderRootSync函数
1. 略去部分步骤,在本系列《触发onClick,react会执行哪些操作》中可以查到
此时已经将concurrentQueues数组中queue的interleaved属性存储的update链表,转移至queue的pending属性上,并且将interleaved清空
2. 下探到本例的函数组件时,调用updateFunctionComponent,会重新执行函数组件从而更新hook,随后调用reconcileChildren进入协调阶段
这一步会跳过update3和update2
3. 协调完本例的函数组件后,后续的下探处理逻辑和commit阶段的所有操作都是无用的,因为本例并没使用isPending
4. 继续执行ensureRootIsScheduled处理下一次调度,此时root.pendingLanes已经移除了DiscreteEventPriority,只剩下TransitionLane1
此时调度一个NormalPriority优先级的宏任务去执行performConcurrentWorkOnRoot
更新hook
全局变量currentlyRenderingFiber$1存储当前workInProgress节点
更新hook的调用链为
updateFunctionComponent
1. useState('a') -> 调用updateState处理hook_flag,此时hook_flag中跳过了update3
2. useTransition() -> updateTransition() -> updateState() 调用updateState处理hook_pending,此时hook_pending中跳过了update2
页面中isPending此时为true
useState -> updateState -> updateReducer
1. hook = updateWorkInProgressHook() 更新currentlyRenderingFiber$1节点的hook
2. current = currentHook 此为current节点hook链表中的useState的hook对象
3. baseQueue = current.baseQueue
4. pendingQueue = hook.queue.pending 此为update链表
5. 将pendingQueue插入current.baseQueue尾部,重新连成环形链表,并且将hook.queue.pending置为null
6. 遍历current.baseQueue
**此部分的代码为react的核心代码,能做到跳过低优先级任务**
---------
第一次更新时,baseQueue链表为空的,经过上述步骤5将pending链表转移到baseQueue中了。如果update链表中有低优先级的update,则需要跳过,构建newBaseQueue
假设此时的baseQueue为update_1(1) -> update_2(64) -> update_3(1) -> update_4(4),括号中的为此update的lane
假设当前处理的lane为1
遍历baseQueue
1. 执行第一次遍历,遇到update_1他的lane为1,优先级足够,将newState赋值为update_1的新值
2. 执行第二次遍历,遇到update_2他的lane为64,优先级不够需要跳过
根据update_2复制一个新的update对象插入newBaseQueue中,并且newBaseState为newState
此时newBaseQueue为 update_2(64)
3. 执行第三次遍历,遇到update_3他的lane优先级足够,但是此时也要根据update_3复制一个新的update对象插入newBaseQueue中,并且newBaseState不重新赋值
从这一步可以看出,只要遇到第一个跳过的update,其后面跟的所有update都需要复制一个,插入到newBaseQueue中。并且newBaseState始终为第一个跳过update的前一个update的新值
此时newBaseQueue为 update_2(64) -> update_3(1)
4. 执行第四次遍历,遇到update_4他的lane为4,优先级不够需要跳过
此时newBaseQueue为 update_2(64) -> update_3(1) -> update_4(4)
---------
1. 赋值
first = baseQueue.next 头节点
newState = current.baseState 旧值
newBaseState = null
newBaseQueueFirst = null
newBaseQueueLast = null
update = first
2. updateLane = update.lane 此时为TransitionLane1
3. 比较全局变量renderLanes & updateLane是否为updateLane,此时renderLanes为DiscreteEventPriority
** false,表明updateLane优先级不够,其对应的update在此次更新中跳过
1. clone = {
lane: updateLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null
}
2. 如果newBaseQueueLast为空
newBaseQueueFirst = newBaseQueueLast = clone
newBaseState = newState
3. 如果newBaseQueueLast不为空
newBaseQueueLast = newBaseQueueLast.next = clone
4. currentlyRenderingFiber$1.lanes = currentlyRenderingFiber$1.lanes | updateLane
** true,表明updateLane优先级足够,其对应的update放在此次更新
1. newBaseQueueLast不为空
1. _clone = {
lane: NoLane,
action: update.action,
hasEagerState: update.hasEagerState,
eagerState: update.eagerState,
next: null
}
2. newBaseQueueLast = newBaseQueueLast.next = _clone
2. update.hasEagerState为true
newState = update.eagerState
3. update.hasEagerState为false
1. action = update.action
2. newState = reducer(newState, action) 计算得出新值
4. update = update.next 继续下次遍历
7. 如果newBaseQueueLast为空,表明没有update跳过
newBaseState = newState
8. 如果newBaseQueueLast不为空,表明有update跳过
newBaseQueueLast.next = newBaseQueueFirst 构建环形链表
9. 赋值
hook.memoizedState = newState
hook.baseState = newBaseState
hook.baseQueue = newBaseQueueLast
queue.lastRenderedState = newState
10. 返回[hook.memoizedState, queue.dispatch]
useTransition -> updateTransition
1. const [isPending] = updateState() 调用updateReducer处理hook_pending
此时hook_pending中存储的queue链表的第一个元素update1的lane为DiscreteEventPriority,正好为此次更新的lane,而第二个元素update2为TransitionLane1需要跳过
所以此时isPending为update1的新值也就是true
2. hook = updateWorkInProgressHook() 更新hook
3. start = hook.memoizedState
4. 返回 [isPending, start]
触发宏任务
performConcurrentWorkOnRoot函数
1. 调用getNextLanes获取lanes,本例为TransitionLane1
2. exitStatus = renderRootSync(root, lanes)
renderRootSync函数
1. 略去部分步骤,在本系列《触发onClick,react会执行哪些操作》中可以查到
2. 下探到本例的函数组件时,调用updateFunctionComponent,会重新执行函数组件从而更新hook,随后调用reconcileChildren进入协调阶段
这一步会处理update3和update2,因为此时处理的lanes为TransitionLane1,而update3和update2的lane都为TransitionLane1,经过更新hook的处理后,flag值为'b',isPending值为false
3. 继续下探,遇到Offscreen节点调用updateOffscreenComponent,执行reconcileChildren对孩子节点协调过程中如果节点是lazy则会执行resolveLazy去加载lazy组件
完成后续的下探和回溯过程,在即将进入commit阶段时,会调用finishConcurrentRender函数
在finishConcurrentRender函数中针对退出状态为RootSuspendedWithDelay的情况,会先执行includesOnlyTransitions判断此次处理的lanes是否是Transition类型的
在本例中,走到这里肯定是Transition类型的,所以直接结束后续执行,未进入commit阶段。产生的效果就是,Suspense中渲染的旧节点不更新
后续的处理逻辑就是执行thenable.then中的函数,得到动态import返回的Module对象将其渲染到Suspense中,在本系列的《react中suspense组件是如何重新渲染的》中有详细介绍
思考
假如动态组件加载的时间过长达到了react中所谓的“饥饿(已过期)”怎么办?
回答此问题,首先要明白为什么会有这种问题出现
因为startTransition中触发的hook都是低优先级的lane,靠后执行,如果在前面高优先级lane的update有很多的话,就容易产生延迟。
其次还要知道怎么判定是否“饥饿(已过期)”
在执行ensureRootIsScheduled调度update时,会为当前update.lane计算对应的过期时间,并存储在root.expirationTimes中,
在调度其他update时会比较root.pendingLanes记录的所有lane的过期时间和当前时间,如果过期了将其合并到root.expiredLanes
在调度update执行完成后,执行到performConcurrentWorkOnRoot函数,在此函数中会判断当前的root.expiredLanes是否包含lanes,如果包含则需要“时间分片”
需要“时间分片”的,会执行renderRootConcurrent,此函数内部逻辑和renderRootSync大差不差
renderRootConcurrent 执行 workLoopConcurrent
renderRootSync 执行 workLoopSync
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
可以看出区别就在于shouldYield函数,在解释shouldYield函数之前需要了解清楚如下调用链
ensureRootIsScheduled
↓
scheduleCallback -> schedulePerformWorkUntilDeadline -> performWorkUntilDeadline -> schedulePerformWorkUntilDeadline <--------|
| |
|-> flushWork -> workLoop -> 同当前时间比较(此处的当前时间会赋值给startTime),如果当前处理的task的expirationTime未超期
|
|-> 超期了,执行task.callback也就是performConcurrentWorkOnRoot
通过上述调用链可以看出,只有超期的task才会进入performConcurrentWorkOnRoot的执行,此时的startTime已经被赋值了,从这里开始执行到shouldYieldToHost函数如果时间间隔大于5证明当前js执行线程卡了
现在再看shouldYield函数的执行逻辑,可以看出当js线程被卡了,workLoopConcurrent中的下探遍历workInProgress的逻辑不执行,然后继续执行ensureRootIsScheduled函数,直到当前js执行线程不卡了才会处理下探遍历的逻辑
var startTime = -1; 在执行performWorkUntilDeadline函数会为startTime赋值
var frameInterval = 5; 这是一帧的时间间隔
function shouldYieldToHost() {
var timeElapsed = now() - startTime;
if (timeElapsed < frameInterval) {
return false;
}
return true;
}
总结:
在root.expiredLanes中的lane都是过期的lane,需要立即顺畅的执行更新,这个时候就不能出现js线程阻塞的问题。运用“时间分片”的逻辑,判断执行时间间隔如果超过5就为线程阻塞
再度执行ensureRootIsScheduled形成一个递归的闭环,直到“时间分片”认为当前线程“空了”,才会处理workInProgress树的下探和回溯
通用函数
mountWorkInProgressHook 函数
1. 创建hook对象
hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
}
2. 全局变量workInProgressHook为空
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook
3. 全局变量workInProgressHook不为空
workInProgressHook = workInProgressHook.next = hook
requestUpdateLane 函数
1. isTransition = ReactCurrentBatchConfig$1.transition !== NoTransition
** 如果isTransition为true
1. transition._updatedFibers.add(fiber)
2. 如果全局变量currentEventTransitionLane为NoLane,其初始值就为NoLanes
currentEventTransitionLane = claimNextTransitionLane()
3. 返回currentEventTransitionLane
** 如果isTransition为false
1. updateLane = currentUpdatePriority
2. 返回 updateLane
enqueueConcurrentHookUpdate 函数
1. interleaved = queue.interleaved 存储update链表
2. 如果interleaved为空
update.next = update
queue存入全局变量concurrentQueues数组中
3. 如果interleaved不为空,将update插入interleaved环形链表中
update.next = interleaved.next
interleaved.next = update
4. queue.interleaved = update
5. 执行并返回markUpdateLaneFromFiberToRoot
markUpdateLaneFromFiberToRoot 函数
这里的fiber是currentlyRenderingFiber$1
1. fiber.lanes = fiber.lanes | lane
2. fiber.alternate.lanes = fiber.alternate.lanes | lane
3. 遍历fiber树,更新父节点的childLanes,操作同步骤1,2
4. 返回root节点
claimNextTransitionLane 函数
1. lane = nextTransitionLane 其中nextTransitionLane初始值为TransitionLane1
2. nextTransitionLane <<= 1 左移一位,如 '1111' -> '11110'
3. 如果nextTransitionLane & TransitionLanes为NoLanes,nextTransitionLane = TransitionLane1
4. 返回lane
entangleTransitionUpdate 函数
判断lane是否为Transition类型的lane(通过判断lane & TransitionLanes是否为NoLanes)
1. entangledLanes = hook.queue.lanes & lane | lane
1. hook.queue.lanes = entangledLanes
2. 执行markRootEntangled函数将entangledLanes添加到root.entanglements数组中
markRootEntangled 函数
1. rootEntangledLanes = root.entangledLanes |= entangledLanes
2. lanes = rootEntangledLanes
3. entanglements = root.entanglements
2. while (lanes > 0)
1. index = pickArbitraryLaneIndex(lanes) 获取lanes的最高位索引
2. lane = 1 << index 获取lanes最高位的二进制数,其余低位全部置零
3. 如果 lane & entangledLanes | entanglements[index] & entangledLanes 为true
entanglements[index] |= entangledLanes
4. lanes &= ~lane 最高位过滤,继续遍历
updateWorkInProgressHook 函数
1. 全局变量currentHook是否为空
** 是
1. current = currentlyRenderingFiber$1.alternate
2. current不为空,nextCurrentHook = current.memoizedState 存储这hook链表
** 否
nextCurrentHook = currentHook.next
2. nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState
3. 如果nextWorkInProgressHook为空
1. currentHook = nextCurrentHook
2. 根据current节点上的hook创建一个新的hook
newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
}
3. 将newHook插入到currentlyRenderingFiber$1.memoizedState链表中
标签:lane,函数,useTransition,update,react,hook,原理,执行,root
From: https://www.cnblogs.com/ye-hcj/p/16822501.html