Skip to content
On this page

首屏初始化

  1. 首屏初始化过程中,生成 FiberRootNode,其current 指针指向应用根节点(tag = 3),根节点 下面是 AppFiberNode
  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. 渲染之后, FiberRootNodecurrent 指针,指向最新生成的 Fiber 树

更新阶段的递

第一次更新

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

createWorkInProgress 逻辑,执行 else 分支

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

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

创建完所有 wipcommit 渲染,并更新 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 都是返回currentalternate ,没有重新生成 FiberNode
  • 根据 child 节点是单个还是 数组,调用不同方法,最终都是返回 alternaete 作为新的 wip 进行下一个 循环更新