react执行流程
2025-08-01 11:38:17 1 举报
AI智能生成
react源码执行流程
作者其他创作
大纲/内容
首次渲染
创建fiberRoot根节点<br>createRoot(container: Container)<br>
通过map来判断当前是否是首次更新<br>containerToRoot
非首次更新,则创建fiberRoot节点,将节点加入map<br>root = createContainer(container)<br> containerToRoot.set(container, root)
创建首个rootFiber子节点,也就是current fiber<br>hostRootFiber = new FiberNode(HostRoot, {}, null)
子主题
创建fiberRoot节点,同时挂载上子节点rootFiber和容器DOM<br>root = new FiberRootNode(container, hostRootFiber)
fiberRoot的pendingPassiveEffects对象保存了unmount和update两个副作用队列<br>fiberRoot的finishedWork保存了已完成任务的fiber节点<br>fiberRoot的pendingLanes保存了未执行的lane集合<br>fiberRoot的finishedLanes保存了本轮更新执行的lane<br>
创建更新队列updateQueue,将更新队列挂载到rootFiber上<br>hostRootFiber.updateQueue = createUpdateQueue<ReactElement>()
返回一个对象,对象有render子节点的方法render和卸载当前节点的方法unmount
调用fiberRoot的render方法,开始渲染<APP/>子节点
初始化节点信息<br>// 实例<br> this.tag = tag;<br> this.key = key;<br> this.stateNode = null;<br> this.type = null;<br><br> // 树结构<br> this.return = null;<br> this.sibling = null;<br> this.child = null;<br> this.index = 0;<br><br> this.ref = null;<br><br> // 状态<br> this.pendingProps = pendingProps;<br> this.memoizedProps = null;<br> this.updateQueue = null;<br> this.memoizedState = null;<br><br> // 副作用<br> this.flags = NoFlags;<br> this.subtreeFlags = NoFlags;<br> this.deletions = null;<br><br> // 调度<br> this.lanes = NoLane;<br> // this.childLanes = NoLanes;<br><br> this.alternate = null;
babel编译JSX并执行,返回ReactElement对象
将生成element传入fiberRoot的render函数,开始渲染 <br>render(element: ReactElement)
清空上次渲染后容器DOM下的对象<br>clearContainerDOM(container)
初始化事件委托,将dispatchEvent绑定到容器DOM上<br>initEvent(container, 'click')
更新容器<br>(包括创建wip rootFiber、创建更新对象、append更新对象、向上冒泡lane优先级、以当前lane优先级调度任务)<br>return updateContainer(element, root)
创建wip rootFiber<br>
根据传入elment创建更新对象update,优先级设置为SyncLane(首次更新,优先级最高)
将更新对象添加到更新队列
将之前保存的更新队列链表断开,将新加入的更新对象接入,然后再接上链表首尾,成为新的环状链表
在当前fiber节点上,开始调度更新<br>scheduleUpdateOnFiber(fiber: FiberNode, lane: Lane)
合并fiber以前的lane和新的lane,然后冒泡到fiberRoot上(方便后续调度lane优先级)<br>markUpdateLaneFromFiberToRoot(fiber: FiberNode, lane: Lane)
合并当前fiber以前的lane和此时传入的新lane(按位或)
向上返回直到rootFiber,然后返回rootFiber.stateNode,也就是fiberRoot
合并新的lane到fiberRoot的pendingLanes上<br>markRootUpdated(root, lane)
在fiberRoot上,以冒泡上来的新的lane优先级调度任务<br>ensureRootIsScheduled(root)
获取fiberRoot.pendingLanes上优先级最高的lane<br>updateLane = getHighestPriorityLane(root.pendingLanes)
如果优先级的lane优先级为Nolane,直接返回<br>
如果优先级最高的lane优先级为SyncLane,则在微任务中调度该优先级<br>
传入同步任务回调函数,并调度同步任务回调函数。<br>scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root, updateLane))<br>(同步任务回调函数就是以当前lane优先级同步执行工作循环performSyncWorkOnRoot.bind(null, root, updateLane))<br>
如果同步更新队列为空则初始化队列,加入任务回调函数
如果同步更新队列不为空,则直接push新的任务回调函数
调度微任务,传入flushSyncCallbacks<br>scheduleMicrotask(flushSyncCallbacks)
返回传入的element<br>
首次挂载时,将根节点的更新任务以最高优先级添加到更新队列中,<br>然后将工作循环函数作为回调函数添加到requestAnimationFrame中,<br>然后浏览器会自动调用回调<br>performSyncWorkOnRoot.bind(null, root, updateLane))
为后续更新做准备工作,例如创建wip,获取优先级<br>prepareFreshStack(root: FiberRootNode, lane: Lane)
复用旧节点current来创建wip节点<br>createWorkInProgress = (current, pendingProps)
如果有旧节点则复用旧节点的<br>pendingProps、subtreeFlags、deletions、type等信息<br>
没有旧节点则新建一个,然后将current和wip通过alternate相互指向
将旧current节点上的updateQueue、flags、child、memoizedProps、memoizedStates、lane赋值到wip上
返回wip
用WorkInProgressRootRenderLane保持当前的lane优先级
循环调用workLoop<br>workLoop()
当wip不为空时循环执行performUnitOfWork(workInProgress)
向下递归协调子节点<br>next = beginWork(fiber, workInProgressRootRenderLane)
将当前节点的优先级初试为0<br>workInProgress.lanes = NoLane
根据wip的tag类型switch调用不同的更函数
HostRoot(rootFiber)<br>执行updateHostRoot(workInProgress, renderLane)
以当前lane优先级调用更新队列,获得最新的baseState<br>workInProgress.memoizedState = processUpdateQueue(<br> baseState,<br> updateQueue,<br> workInProgress,<br> renderLane<br> );
如果旧的节点上的更新队列不为空,则获取旧的更新队列<br>
遍历旧的更新队列链表,依次获取每个更新对象的优先级,<br>如果优先级等于当前渲染的优先级,则传入baseSate执行更新对象中的更新函数获取新的baseState
如果更新对象的action是函数则传入旧的baseState得到新的baseState,<br>如果不是函数,那就是传入的数据,直接复制给baseState
返回最新的baseState<br>
nextChildren = workInProgress.memoizedState(更新要用的JSX对象)
协调子节点<br>reconcileChildren(workInProgress, nextChildren)
如果wip对于的旧节点current存在,则需要协调旧节点的子节点来复用<br>workInProgress.child = reconcileChildFibers(<br> workInProgress,<br> current.child,<br> children<br> );
如果子节点是对象,则表示孩子节点只有一个,调用协调单节点的函数复用单节点,<br>然后根据是否复用给当前单节点打上Placement更新标记<br>placeSingleChild(<br> reconcileSingleElement(returnFiber, currentFirstChild, newChild)<br> )
reconcileSingleElement(returnFiber, currentFirstChild, newChild)<br>协调复用单个子节点并返回该复用节点或者新节点<br>
// 前:abc 后:a 删除bc<br> // 前:a 后:b 删除b、创建a<br> // 前:无 后:a 创建a<br>从第一个旧的孩子节点开始遍历,与当前唯一的一个新节点比较<br>1.如果key相同type相同则可以复用,别忘了删除不可复用的兄弟节点<br>2.如果key相同type不同则不可复用<br>3.如果key不同则删除旧的,继续比较下一个旧节点和当前新的节点<br>4.如果没有找到可以复用的旧节点则根据element对象创建一个新的节点,并返回<br>
将协调得到的单一子节点传入当前函数,如果该节点不是复用的节点,则打上Placement副作用标记
如果子节点是数组则调用协调多个子节点的函数<br>reconcileChildrenArray(returnFiber, currentFirstChild, newChild)
创建map用于存储旧节点,方便快速查找旧节点和新节点对比<br>
循环遍历newChild数组,协调每一个节点<br>
利用map存储旧节点,key->fiber。然后利用新节点的key来查找对比,<br>决定是复用旧节点还是根据element新建新节点。<br>// after对应的fiber,可能来自于复用,也可能是新建<br> const newFiber = updateFromMap(<br> returnFiber,<br> existingChildren,<br> i,<br> after<br> ) as FiberNode;
如果协调的子节点tag类型为number、string或者为空<br>作为优化手段,则复用其在数组中的下标即可,不复用key,<br>
如果协调的子节点非字符串等类型,则复用key效率更高
从map中获取旧节点的key
如果协调的子节点为number、string或者为空<br>作为优化手段,如果从map中可以获取到旧文本节点则复用,<br>如果从map中获取不了旧文本节点则新建文本节点<br>
如果协调的子节点tag类型为对象,若map中可以找到旧节点并且新机会节点的type一样<br>则复用旧节点,否则删除旧节点并根据element创建新节点<br>
将新节点和上一个兄弟节点链接到一起
根据环境变量shouldTrackEffects,判断是首次挂载(false)还是更新(true),<br>首次挂载则不需要在进行位置相关的diff操作
非首次挂载,则需要进行位置的diff操作<br>首先从map中获取到旧的节点的oldIndex<br>如果oldIndex < lastPlacedIndex<br>则需要向右移动,标记flags为Placement<br>如果oldindex>lastPlacedIndex<br>则可以复用<br>
获取不了旧的index,则直接插入,标记flags为Placement
遍历结束后删除map中的引用,避免闭包<br>
协调完整个数组后,返回第一个子节点<br>
如果子节点是string或者number类型,则调用文本节点协调函数,<br>然后根据是否复用给当前节点打上Placement更新标记<br>reconcileSingleTextNode(returnFiber, currentFirstChild, newChild + '')
其它情况则全部视为删除旧节点
current不存在,则是首次挂载<br>mountChildFibers(workInProgress, null, children)
返回协调复用得到的子节点
HostComponent(DOM元素对应的fiber)<br>updateHostComponent(workInProgress)<br><br>
根据element传入到workInProgress中的pendingProps中的children,<br>继续向下协调复用子节点<br>reconcileChildren(workInProgress, nextChildren)
HostText(文本DOM元素对象的fiber)<br>直接返回return null<br>
FunctionComponent(函数组件对应的fiber)<br>updateFunctionComponent(workInProgress, renderLane)
执行函数组件,添加hooks到hooks队列,同时返回子节点<br>nextChildren = renderWithHooks(workInProgress, renderLane)
更新当前渲染的fiber<br>currentlyRenderingFiber = workInProgress
重置wip上的缓存状态和更新队列,将当前渲染的lane更新为传入的lane<br>workInProgress.memoizedState = null;<br> workInProgress.updateQueue = null;<br> renderLane = lane;
如果旧节点不为空,则要调用的hooks队列为更新时的hooks队列<br>currentDispatcher.current = HooksDispatcherOnUpdate
const HooksDispatcherOnMount: Dispatcher = {<br> useState: mountState,<br> useEffect: mountEffect<br>}
如果旧节点为空,则要调用的hooks队列为挂载时的hooks队列<br>currentDispatcher.current = HooksDispatcherOnMount
const HooksDispatcherOnUpdate: Dispatcher = {<br> useState: updateState,<br> useEffect: updateEffect<br>}
以当前lane优先级,执行函数组件,添加hooks到对应的队列中,返回子节点<br>const Component = workInProgress.type;<br> const props = workInProgress.pendingProps;<br> const children = Component(props);
执行函数组件,添加hooks到对应的队列中<br>以当前lane优先级,执行函数组件,添加hooks到对应的队列中,返回子节点<br>const children = Component(props);<br>
执行函数组件上的useState(initialState)<br>实际就是执行HooksDispatchOnUpdate上的<br>mountState或者updateState<br>
function mountState<State>(<br> initialState: (() => State) | State<br>): [State, Disptach<State>]
创建最新的hook对象,挂载到全局hooks链表上,返回这个新的hook对象<br>hook = mountWorkInProgressHook()<br><br>
创建一个hook对象<br>const hook: Hook = {<br> memoizedState: null,<br> updateQueue: null,<br> next: null<br> }
如果是首次挂载,将新hook挂载到当前渲染的fiber上,并更新workInProgressHook指针<br>currentlyRenderingFiber.memoizedState = workInProgressHook = hook
如果是更新,则将新hook链接到workInProgressHook后面<br>然后移动workInProgressHook指针到最新的hooks上<br>workInProgressHook = workInProgressHook.next = hook;
返回最新的hook
如果传入的initialState是函数,则执行它获得新的memoizedState
如果传入的initialState是变量,则直接复制给memoizedState<br>
将计算得到的新的memoizedState存入hook.memoizedState<br>hook.memoizedState = memoizedState;
创建更新队列,用于初始化hook.updateQueue<br>const queue = createUpdateQueue<State>();<br> hook.updateQueue = queue;
给SetState绑定调用参数,包括调用时的fiber,对应的更新队列<br>const dispatch = (queue.dispatch = dispatchSetState.bind(<br> null,<br> currentlyRenderingFiber,<br> queue<br> ))<br>
返回最新的state值和setState函数
执行函数组件上的useEffect(create, deps)<br>执行函数组件上的useState(initialState)<br>实际就是执行HooksDispatchOnUpdate上的<br>mountEffect或者updateEffect<br>
创建最新的hook对象,挂载到全局hooks链表上,返回这个新的hook对象<br>hook = mountWorkInProgressHook()<br><br>
创建一个hook对象<br>const hook: Hook = {<br> memoizedState: null,<br> updateQueue: null,<br> next: null<br> }
如果是首次挂载,将新hook挂载到当前渲染的fiber上,并更新workInProgressHook指针<br>currentlyRenderingFiber.memoizedState = workInProgressHook = hook
如果是更新,则将新hook链接到workInProgressHook后面<br>然后移动workInProgressHook指针到最新的hooks上<br>workInProgressHook = workInProgressHook.next = hook;
返回最新的hook
给当前渲染的节点打上PassiveEffect标记<br>// 注意区分PassiveEffect与Passive,PassiveEffect是针对fiber.flags<br> // Passive是effect类型,代表useEffect。类似的,Layout代表useLayoutEffect<br> (currentlyRenderingFiber as FiberNode).flags |= PassiveEffect;
给新创建的hook对象上保持的effect链表添加新的effect对象<br>hook.memoizedState = pushEffect(<br> Passive | HookHasEffect,<br> create,<br> undefined,<br> nextDeps<br> )
根据传入的信息创建一个新的effect对象<br>
判断当前渲染节点上是否存在旧的effect链表updateQue<br>,如果存在则断开旧链表,接上新节点,然后合并成新的环状链表
如果当前渲染节点上不存在旧的effect链表updataQue,<br>则创建新的updateQue,接上新的链表节点,节点自己成环
返回更新后的effect链表<br>
退出前重置一些全局变量<br>currentlyRenderingFiber = null;<br> workInProgressHook = null;<br> currentHook = null;<br> renderLane = NoLane;
返回得到的子节点
协调子节点<br>reconcileChildren(workInProgress, nextChildren)
如果wip对于的旧节点current存在,则需要协调旧节点的子节点来复用<br>workInProgress.child = reconcileChildFibers(<br> workInProgress,<br> current.child,<br> children<br> );
如果子节点是对象,则表示孩子节点只有一个,调用协调单节点的函数复用单节点,<br>然后根据是否复用给当前单节点打上Placement更新标记<br>placeSingleChild(<br> reconcileSingleElement(returnFiber, currentFirstChild, newChild)<br> )
reconcileSingleElement(returnFiber, currentFirstChild, newChild)<br>协调复用单个子节点并返回该复用节点或者新节点<br>
// 前:abc 后:a 删除bc<br> // 前:a 后:b 删除b、创建a<br> // 前:无 后:a 创建a<br>从第一个旧的孩子节点开始遍历,与当前唯一的一个新节点比较<br>1.如果key相同type相同则可以复用,别忘了删除不可复用的兄弟节点<br>2.如果key相同type不同则不可复用<br>3.如果key不同则删除旧的,继续比较下一个旧节点和当前新的节点<br>4.如果没有找到可以复用的旧节点则根据element对象创建一个新的节点,并返回<br>
将协调得到的单一子节点传入当前函数,如果该节点不是复用的节点,则打上Placement副作用标记
如果子节点是数组则调用协调多个子节点的函数<br>reconcileChildrenArray(returnFiber, currentFirstChild, newChild)
如果子节点是string或者number类型,则调用文本节点协调函数,<br>然后根据是否复用给当前节点打上Placement更新标记<br>reconcileSingleTextNode(returnFiber, currentFirstChild, newChild + '')
其它情况则全部视为删除旧节点
current不存在,则是首次挂载<br>workInProgress.child = mountChildFibers(workInProgress, null, children)<br>mountChildFibers()函数和reconcileChildFibers()引用的是同一个函数,只是传入的闭包shouldTrackEffects<br>变量不一样,通过该变量可以区分是首次挂载还是更新<br>
还是调用一样的函数,只是表示环境变量的闭包值不一样<br>reconcileChildFibers(<br> workInProgress,<br> current.child,<br> children<br> );
返回协调复用后的子节点<br>return workInProgress.child
执行完beginWork后,pendingProps 变为 memoizedProps<br>fiber.memoizedProps = fiber.pendingProps
如果next不为空,则将wip指针更新为最新的next,<br>循环执行workLoop,继续向下协调<br>workInProgress = next
如果next为空,则表示递归到底层,开始向上来协调兄弟节点<br>completeUnitOfWork(fiber)
循环执行completeWork,顺序如下<br>从叶子节点开始向上返回,首先遍历兄弟节点,<br>遍历完兄弟节点后返回父节点,<br>循环上述过程,直到返回rootFiber根节点<br>
completeWork(node)<br>根据fiber的tag switch调用相应的处理函数
HostComponent(DOM元素对应的fiber)<br>
如果旧节点current存在并且对应的DOM节点也存在<br>则复用DOM,调用更新props的函数<br>updateFiberProps(workInProgress.stateNode, newProps)
根据新props中的属性和回调函数更新旧DOM<br>
如果上述条件不成立,则新建一个DOM对象<br>
将子节点挂载到DOM上<br>appendAllChildren(instance, workInProgress)
将新建的DOM子节点挂载到父节点上,<br>要注意的是只有DOM原生节点可以挂载,<br>非原生节点(组件fiber)则找其子树上的原生节点挂载<br>
冒泡flag<br>bubbleProperties(workInProgress)
从第一个子节点开始循环遍历子节点链表<br>1.将子节点的flags、subtreeFlags合并到父节点的subtreeFlags上<br>2.将新建的子节点链表中后续的兄弟节点全部链接到父节点上(建子节点链表时只链接的sibling,没链接return)<br>
HostRoot(rootFiber)
冒泡flag<br>bubbleProperties(workInProgress)
HostText(文本DOM元素对象的fiber)
如果旧节点current存在并且对应的DOM文本节点也存在<br>则复用DOM文本节点,如果新旧文本相同则直接复用,<br>如果新旧文本不同则调用markUpdate(workInProgress)
如果上述条件不满足,则新建一个DOM文本节点<br>textInstance = createTextInstance(newProps.content);<br> workInProgress.stateNode = textInstance;
冒泡flag<br>bubbleProperties(workInProgress)
FunctionComponent(函数组件对应的fiber)
冒泡flag<br>bubbleProperties(workInProgress)
所有case都return null<br>
获取上一次的执行上下文<br>executionContext = prevExecutionContext<br>初始化全局渲染优先级<br>workInProgressRootRenderLane = NoLane<br>const finishedWork = root.current.alternate<br> root.finishedWork = finishedWork<br> root.finishedLane = lane
// commit阶段操作<br> commitRoot(root)
markRootFinished(root, lane)
将当前节点的flag以及subtreeFlags与PassiveMask按位与,得出该子树是否有副作用<br>如果有副作用并且rootDoesHavePassiveEffects标记为false,则修改为true,<br>然后以普通优先级调度异步任务<br>rootDoesHavePassiveEffects = true;<br> scheduleCallback(NormalSchedulerPriority, () => {<br> flushPassiveEffects(pendingPassiveEffects);<br> return;<br> })
以当前优先级调度新的任务<br>unstable_scheduleCallback(priorityLevel, callback, options)
获取当前时间currentTime <br>根据优先级priorityLevel switch选择优先级对应的延迟时间<br>计算任务过期时间expirationTime = startTime + timeout<br>新建一个任务对象var newTask = {<br> id: taskIdCounter++,<br> callback: callback,<br> priorityLevel: priorityLevel,<br> startTime: startTime,<br> expirationTime: expirationTime,<br> sortIndex: -1<br> }<br>
如果当前任务为延迟任务,则加入延迟队列timerQueue<br>如果当前有任务执行需要调用取消函数cancelHostTimeout();<br>调度当前延迟任务requestHostTimeout(handleTimeout, startTime - currentTime)
如果当前任务为过期任务,则加入任务队列taskQueue,<br>如果当前没有任务在执行则调度该过期任务requestHostCallback(flushWork)
返回当前任务对象<br>return newTask<br>
将当前节点的flags、subtreeFlags和副作用掩码按位与<br>得出该子树是否存在某个节点有副作用<br>
如果子树中有节点有副作用<br>更改执行环境为CommitContext<br>executionContext |= CommitContext
beforeMutation阶段:<br>遍历执行相关的hooks链表<br>
Mutation阶段:<br>遍历执行相关hooks链表<br>commitMutationEffects(finishedWork, root)
从父节点开始向下深度遍历,<br>通过subtreeFlags找到有副作用的子节点,然后更新子节点<br>commitMutationEffectsOnFiber(nextEffect, root)<br>遍历子节点的兄弟节点,直到最后一个兄弟几点,此时返回到父节点<br>
commitMutationEffectsOnFiber(nextEffect, root)
获取nextEffect的flags<br>根据flags执行对应的操作<br>
Placement执行插入或者移动<br>commitPlacement(finishedWork)
找到待插入节点的DOM容器节点<br>hostParent = getHostParent(finishedWork)
向上查找找到tag为<br>HostComponent(DOM元素的tag)或<br>HostRoot(rootFiber的tag)<br>
找到带插入节点的DOM兄弟节点<br>sibling = gethostSibling(finishedWork)
难点在于目标fiber的hostSibling可能并不是他的同级sibling<br> * 比如: <A/><B/> 其中:function B() {return <div/>} 所以A的hostSibling实际是B的child<br> * 实际情况层级可能更深<br> * 同时:一个fiber被标记Placement,那他就是不稳定的(他对应的DOM在本次commit阶段会移动),也不能作为hostSibling
找到了待插入节点的父节点和兄弟节点就可以插入了<br>insertOrAppendPlacementNodeIntoContainer(finishedWork, hostParent, sibling)
如果是DOM类型的节点直接insert或者append<br>insertChildToContainer(fiber.stateNode, parent, before)<br> appendChildToContainer(fiber.stateNode, parent)<br>
如果节点是组件即节点则递归调用该函数,插入节点改为其子节点<br>如果有洗兄弟节点则处理兄弟节点<br>insertOrAppendPlacementNodeIntoContainer(child, parent, before);<br> let sibling = child.sibling;<br><br> while (sibling !== null) {<br> insertOrAppendPlacementNodeIntoContainer(sibling, parent, before);
ChildDeletion执行删除操作<br>commitDeletion(childToDelete, root)
Update执行更新操作<br>commitUpdate(finishedWork)
PassiveEffect则收集因为deps变化而需要执行的Effect<br>commitPassiveEffect(finishedWork, root, type)<br>
root.pendingPassiveEffects[type].push(updateQueue.lastEffect as Effect)
Mutation执行完,切换Fiber Tree<br>root.current = finishedWork;
Layout阶段
commit执行完,更改执行环境为之前的环境<br>executionContext = prevExecutionContext
还原全局副作用标记<br>rootDoesHavePassiveEffects = false;
点击事件触发更新<br>
setState(newState)
function dispatchSetState<State>(<br> fiber: FiberNode,<br> updateQueue: UpdateQueue<State>,<br> action: Action<State><br>)
获取更新时的优先级<br>lane = requestUpdateLane()
根据当前优先级创建一个更新对象<br>update = createUpdate(action, lane)
将更新对象添加入更新队列<br>enqueueUpdate(updateQueue, update)
在当前fiber节点上,开始调度更新<br>scheduleUpdateOnFiber(fiber: FiberNode, lane: Lane)
合并fiber以前的lane和新的lane,然后冒泡到fiberRoot上(方便后续调度lane优先级)<br>
合并当前fiber以前的lane和此时传入的新lane(按位或)
向上返回直到rootFiber,然后返回rootFiber.stateNode,也就是fiberRoot
合并新的lane到fiberRoot的pendingLanes上<br><br>
在fiberRoot上,以冒泡上来的新的lane优先级调度任务<br>ensureRootIsScheduled(root)
获取fiberRoot.pendingLanes上优先级最高的lane
如果优先级的lane优先级为Nolane,直接返回<br>
如果优先级最高的lane优先级为SyncLane,则在微任务中调度该优先级<br>
传入同步任务回调函数,并调度同步任务回调函数。<br>(同步任务回调函数就是以当前lane优先级同步执行工作循环performSyncWorkOnRoot.bind(null, root, updateLane))<br>
如果同步更新队列为空则初始化队列,加入任务回调函数
如果同步更新队列不为空,则直接push新的任务回调函数
调度微任务,传入flushSyncCallbacks
0 条评论
下一页
为你推荐
查看更多