触发渲染过程------renderRoot
renderRoot
是一个函数,用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务,直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork,以及渲染新状态或 DOM。
bash
function renderRoot(root: FiberRootNode) {
//双缓存机制,将current复制一层给workInProgress
//React 使用两棵 Fiber 树(current 和 workInProgress)来实现双缓存
const { current } = root; // 获取当前的 Fiber 树的根节点
let workInProgress = current;
// 启动渲染任务(并发渲染模式下会启动任务)
workInProgress = performUnitOfWork(workInProgress);
// 继续调度工作单元
while (workInProgress !== null) {
workInProgress = performUnitOfWork(workInProgress);
}
}
renderRoot
React 的渲染过程可以分为多个阶段,包括:更新(Reconciliation)、渲染(Rendering)、提交(Commit)等。
bash
//更细节的
function renderRoot(root: FiberRootNode) {
try {
// 初始化
prepareFreshStack(root);
// 开始工作循环
do{
try{
workLoop();
break;
} catch (e) {
console.warn('workLoop发生错误', e);
workInProgress = null;
}while (true);
// 获取完成的工作单元
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork;
// 提交根节点的wip fiberNode树,并处理flags
commitRoot(root);
prepareFreshStatck()
此prepareFreshStatck()
中的createWorkInProgress简单理解就是上面将current复制一层给workInProgress, const { current } = root; // 获取当前的 Fiber 树的根节点 let workInProgress = current;
,两棵 Fiber 树来实现双缓存
。
复制过后的双缓存 数据结构
workLoop()
bash
function workLoop(){
while(workInProgress !== null)
performUnitOfWork(workInProgress);
}
performUnitOfWork(workInProgress) 【递归】
负责递归遍历 Fiber 树,并根据不同的 Fiber 类型执行相应的更新或渲染逻辑
bash
function performUnitOfWork(fiber: FiberNode) {
// 开始对当前Fiber节点进行工作
// 这里的"递"可能指的是递归处理子节点
const next = beginWork(fiber); // 执行beginWork函数,返回下一个需要处理的Fiber节点
// 更新当前Fiber节点的memoizedProps为pendingProps
// 这通常意味着将传入的props"确认"为当前节点的属性
fiber.memoizedProps = fiber.pendingProps;
// 判断下一个需要处理的节点是否为null
// 如果为null,可能表示当前节点没有子节点或子节点已经处理完毕
if (next === null) {
// 这里的"归"可能指的是回溯到父节点,完成当前节点的工作
completeUnitOfWork(fiber); // 执行completeUnitOfWork函数,完成当前Fiber节点的工作
} else {
// 如果还有下一个节点需要处理,则更新workInProgress指针
// workInProgress通常指向当前正在处理的Fiber节点
workInProgress = next; // 更新workInProgress为下一个需要处理的Fiber节点
// 注意:这里通常会有一个递归调用performUnitOfWork(next),但在您提供的代码片段中省略了
}
}
递归结束
,状态模型
:
beginWork流程(递)
- 建立节点的父子以及兄弟节点关联关系
child return sibling
属性 - 给fiber节点打上
flag标记
(当前节点的flag)
beginWork 主要发生在协调阶段,它被调用来处理和更新 React 虚拟 DOM(Fiber 树)。根据节点类型(HostRoot、FunctionComponent等)调用对应更新函数
1.初始化 Fiber 节点的工作:当 React 开始处理某个 Fiber 节点时,beginWork 会被调用。它会检查该节点的当前状态,并决定是否需要进行更新。
2.调度子节点的更新:如果当前节点有子节点,beginWork 会为这些子节点安排进一步的更新工作。
在执行过程中,beginWork 会遍历 Fiber 树,判断是否需要更新(例如,检查 props 或 state 是否发生了变化),然后决定是否继续向下渲染(即继续对子节点调用 beginWork)。
bash
export const beginWork = (wip: FiberNode): FiberNode | null => {
// 根据Fiber节点的类型,执行相应的更新逻辑,并返回下一个需要处理的Fiber节点
switch (wip.tag) {
case 'HostRoot':
return updateHostRoot(wip); // 处理根节点
case 'HostComponent':
return updateHostComponent(wip); // 处理宿主组件(如DOM元素)
case 'HostText':
return null; // 文本节点不需要进一步处理,直接返回null
case 'FunctionComponent':
return updateFunctionComponent(wip); // 处理函数组件
default:
console.warn('beginWork未实现的类型'); // 输出警告信息
break; // 注意:这里的break是多余的,因为return已经会退出函数
// 但为了保持格式一致性和清晰性,我们暂时保留它
return null; // 对于未实现的类型,返回null
}
beginWork初次执行完, 此时内存状态,wip和h1对应的fiber对象建立联系,并且给h1 fiber打上flags标记 。
updateHostRoot
更新队列(processUpdateQueue【memorizedState=element元素】)、协调子元素(reconcileChildren)
bash
function updateHostRoot(wip: FiberNode): FiberNode | null {
// 获取当前工作单元(Fiber)的基态
const baseState = wip.memoizedState as Element; // 假设memoizedState是Element类型
// 获取更新队列,并断言其类型为UpdateQueue<Element>
const updateQueue = wip.updateQueue as UpdateQueue<Element>;
// 从共享对象中取出待处理的更新,并清空待处理队列
const pending = updateQueue.shared.pending;
updateQueue.shared.pending = null;
// 使用processUpdateQueue函数处理待处理的更新,并获取更新后的状态
//执行函数获取element对象
const { memoizedState } = processUpdateQueue(baseState, pending) as { memoizedState: Element };
// 更新当前工作单元的基态为最新状态
wip.memoizedState = memoizedState;
// 获取更新后的子节点,这里假设memoizedState直接代表了子节点
// 注意:这里的逻辑可能需要根据实际情况调整,因为memoizedState可能并不直接等于子节点
const nextChildren = wip.memoizedState as Element[]; // 假设这里是Element数组,但需要根据实际情况确定
// 调用reconcileChildren函数来协调(渲染)子节点
// 注意:函数名可能是reconcileChildren的一个拼写错误,通常应该是reconcileChildren或者类似的名称,但这里按照您提供的名称使用
reconcileChildren(wip, nextChildren);
// 返回当前工作单元的第一个子节点,以便后续的工作单元可以继续处理
// 注意:如果wip.child是null,则表示没有子节点需要处理
return wip.child;
}
processUpdateQueue
bash
export const processUpdateQueue = <state>(
baseState: state,
pendingUpdate: Update<state> | null
): { memoizedState: state } => {
// 初始化结果对象,其memoizedState属性设置为baseState
const result: { memoizedState: state } = {
memoizedState: baseState
};
// 检查是否有待处理的更新
if (pendingUpdate !== null) {
const action = pendingUpdate.action;
// 如果action是一个函数,则执行它并更新memoizedState
if (typeof action === 'function') {
result.memoizedState = action(baseState);
} else {
// 如果action不是函数,则直接将其值赋给memoizedState
// 注意:这里假设action的类型与state兼容
result.memoizedState = action as state; // 需要类型断言来确保TypeScript不会报错
}
}
// 返回结果对象
return result;
};
** 注:Fiber对象数据结构 **
reconcileChildren(★★★)
reconcileChildren 主要处理组件的子树,对于每一个子节点(即子 Fiber 节点)会执行以下操作:
子节点的类型判断
(会首先判断每个子节点的类型【比如是 DOM 元素、函数组件还是类组件等】,然后根据不同的类型来决定如何处理)节点的比较
(相同类型的节点/不同类型的节点/key 和索引
)生成新的 Fiber 节点
(为需要更新或新创建的子组件生成新的 Fiber 节点)处理子树的递归(beginWork中递归调用)
(会递归地调用自己来处理子组件。如果某个子组件有子节点,React 会继续对子节点进行协调,直到所有节点都被处理完)
reconcileChildFibers|mountChildFibers
创建子fiber的过程会进入reconcileChildren,该函数的作用是为workInProgress fiber节点生成它的child fiber即 workInProgress.child。然后继续深度优先遍历它的子节点执行相同的操作。mountChildFibers,reconcileChildFibers和mountChildFibers最终其实就是ChildReconciler
传递不同的参数返回的函数,这个参数用来表示是否追踪副作用
.
bash
function ChildReconciler(shouldTrackSideEffects) {
function placeChild(newFiber, lastPlacedIndex, newIndex) {
newFiber.index = newIndex;
if (!shouldTrackSideEffects) {//是否追踪副作用
// Noop.
return lastPlacedIndex;
}
var current = newFiber.alternate;
if (current !== null) {
var oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
// This is a move.
newFiber.flags = Placement;
return lastPlacedIndex;
} else {
// This item can stay in place.
return oldIndex;
}
} else {
// This is an insertion.
newFiber.flags = Placement;
return lastPlacedIndex;
}
}
}
bash
const App:any=function (){
return(
<h1>
<h2>
<h3>3333</h3>
</h2>
</h1>
)
}
初次被调用执行, 此时内存状态,wip和h1对应的fiber对象建立联系,并且给h1 fiber打上flags标记 。
递归,直至next指向为null
completeWork流程(归)
主要执行任务:
1.
创建真实dom节点
,但是仍在内存中,未渲染到页面2.
处理flag与subtreeFlags
(标记
子树标识,用"|"
运算处理)3.
建立真实DOM关系
,将子元素插入父元素中
bash
function completeWork(current, workInProgress) {
switch (workInProgress.tag) {
case 'HostComponent': {
// 如果是普通的 DOM 节点
if (!workInProgress.stateNode) {
// 如果没有对应的 DOM 实例,创建一个新的
const domElement = document.createElement(workInProgress.type);
// 为 DOM 元素添加属性
const props = workInProgress.pendingProps;
for (const key in props) {
if (key === 'children') {
// 如果是文本内容,直接设置
if (typeof props[key] === 'string' || typeof props[key] === 'number') {
domElement.textContent = props[key];
}
} else if (key.startsWith('on')) {
// 添加事件监听器(如 onClick)
const eventType = key.toLowerCase().substring(2);
domElement.addEventListener(eventType, props[key]);
} else {
// 设置其他属性
domElement.setAttribute(key, props[key]);
}
}
// 将 DOM 实例存储在 stateNode 中
workInProgress.stateNode = domElement;
}
return null;
}
case 'FunctionComponent':
case 'ClassComponent': {
// 函数组件和类组件在 completeWork 中通常不需要特殊处理
return null;
}
default:
return null;
}
}