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