Skip to content
On this page

首屏初始化

  1. 首屏初始化过程中,生成 FiberRootNode,其current 指针指向应用根节点(tag = 3),根节点 下面是 App 的 FiberNode
  2. 首屏渲染启动,生成 workInProgress(wip), createWorkInProgress(root.current),参数是上面 1 生成的 根节点FiberNode
js
function createWorkInProgress(current, pendingProps){
	let workInProgress = current.alternate // 获取 根节点 的alternate
	if (workInProgress === null) {
		// 首屏渲染为空的逻辑
		// 根据当前根节点,创建 wip
    workInProgress = createFiber(current.tag); 
    // wip,createFiber返回一个要渲染的 FiberNode
		// ...一些 current属性复制给 wip
	} else {
		// 更新分支逻辑
		// 因为更新时有缓存之前的 FiberNode, 直接复用之前node的属性,不重新创建
	}
	workInProgress.lanes = current.lanes;
	workInProgress.child = current.child;
	workInProgress.memoizedProps = current.memoizedProps;
	workInProgress.memoizedState = current.memoizedState;
	workInProgress.updateQueue = current.updateQueue;
	// ... 将current的属性重新赋值到wip
	return workInProgress;
}
  1. 返回的 wip(根节点),通过 performUnitOfWork 进行 子节点 的生成,并更新 wip
js
function performUnitOfWork(unitOfWork) {
	//... unitOfWork 其实是当前 wip
	let next
	next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
	// 返回 childFiberNode ...
	workInProgress = next 
	// 最后更新 wip,外层 workLoopSync 循环继续更新子节点
}
  1. 生成 所有子节点 之后进行 commit进行渲染到实际 DOM
  2. 渲染之后, FiberRootNode 的 current 指针,指向最新生成的 Fiber 树

更新阶段的递

第一次更新

触发更新的时候,是从 根节点(rootFiber)开始往下更新

在 createWorkInProgress 逻辑,执行 else 分支

然后 重复执行 createWorkInProgress 流程,更新 wip

注意:更新过程中,新创建的 wip 会沿用原 current 的属性字段(如 child) 后续更新中,child 也会创建新的 wip

创建完所有 wip ,commit 渲染,并更新 current 指针

小结

第一次更新之后,会成功创建出两颗 FiberNode 树

第二次更新

第二次更新过程,每次 createWorkInProgress 创建 wip ,其 current 都有 alternate,实现双缓存

  1. 第二次更新过程中,FiberNode都有对应的 current ,生成wip 执行beginwork,执行不同逻辑
js
// 更新 rootFiber 过程
function beginWork(current, workInProgress) {
	// wip 对比 current的属性没有变化,复用节点
	didReceiveUpdate = false; // 标记该 FiberNode 是否有更新
  return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);

	// App 节点逻辑
	didReceiveUpdate = false;
	switch (workInProgress.tag) {
		case FunctionComponent:
      {
	      // 也就是传入 App组件 构造函数
        var Component = workInProgress.type;
        var unresolvedProps = workInProgress.pendingProps;
        var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps);
        // 更新函数式组件
        return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes);
      }
	}
}
// 内部调用
function bailoutOnAlreadyFinishedWork(current, workInProgress) {
	//... 
	cloneChildFibers(current, workInProgress);
	return workInProgress.child
	// 返回 wip 子节点到 performUnitOfWork 作为 next
}

// 复用节点优化逻辑,复制所有子节点
function cloneChildFibers(current, workInProgress) {
  if (workInProgress.child === null) {
    return;
  }
  var currentChild = workInProgress.child;
  // 根据子节点创建FiberNode
  var newChild = createWorkInProgress(currentChild, currentChild.pendingProps)
  // 更新wip 的child
  workInProgress.child = newChild;
  newChild.return = workInProgress;
	// 子兄弟节点复制
  while (currentChild.sibling !== null) {
    currentChild = currentChild.sibling;
    newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps);
    newChild.return = workInProgress;
  }

  newChild.sibling = null;
} 
  1. rootFiber 会复用原有的属性,执行bailoutOnAlreadyFinishedWork 返回子节点App
  2. App 节点,通过 updateFunctionComponent 更新节点
js
function updateFunctionComponent(current, wip, Component, nextProps) {
	// ...
	// 内部执行 Component 构造函数,返回jsx对象
	 nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
	 // ...
	 workInProgress.flags |= PerformedWork;
	 // 子节点child更新
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;// 返回上层进行下一个更新
}
// 调用构造函数
function renderWithHooks(current, wip, Component, nextProps, secondArg) {
	//...
	const children = Component(props, secondArg); // 返回App 的 jsx 对象
	//...
	return children
}


function reconcileChildren(current, wip, nextChildren) {
	// 由于有current,reconcileChildFibers 标记 flags 为true
	reconcileChildFibers(wip, current.child, nextChildren)
	// 1. reconcileChildren
	// 2. reconcileSingleElement
	// 3. useFiber
	// 4. createWorkInProgress
	// 5. placeSingleChild
	// 双缓存,返回 child.alternate 
}

function reconcileSingleElement(returnFiber, currentFirstChild, element) {
	// ...
	while (child !== null) {
		const _existing = useFiber(child, element.props); 
		// 内部调用createWorkInProgress
		_existing.return = returnFiber;
	 return _existing;
	}
}

function placeSingleChild(newFiber) {
	  // 只有是 current没有的 FiberNode,只在当前wip下新增的会加 flags
    if (shouldTrackSideEffects && newFiber.alternate === null) {
      newFiber.flags |= Placement;
    }
    return newFiber;
  }
// 返回 App 下的子节点

小结

  • 第二次更新过程中,由于双缓存机制,每次wip 都是返回current 的 alternate ,没有重新生成 FiberNode
  • 根据 child 节点是单个还是 数组,调用不同方法,最终都是返回 alternaete 作为新的 wip 进行下一个 循环更新