Skip to content

completeWork 阶段

performUnitOfWork的第二步就是completeWork阶段,每次都会执行completeUnitOfWork方法:

javascript
function performUnitOfWork(unitOfWork: Fiber): void {
 // ...
  if (next === null) {
    // 如果没有 next child,那么开始 complete.
    // 创建可真实节点
    completeUnitOfWork(unitOfWork);
  } else {
    // 如果存在 next,说明还有 child,继续向下递归 beginWork
    workInProgress = next;
  }
}
function performUnitOfWork(unitOfWork: Fiber): void {
 // ...
  if (next === null) {
    // 如果没有 next child,那么开始 complete.
    // 创建可真实节点
    completeUnitOfWork(unitOfWork);
  } else {
    // 如果存在 next,说明还有 child,继续向下递归 beginWork
    workInProgress = next;
  }
}

bubbleProperties

对于所有的fiber都会进行bubbleProperties处理。该函数的作用是遍历workInProgress的第一层子节点,将所有child.laneschild.childLanes合并到当前的childLanes上。将所有child.subtreeFlagschild.flags合并到当前subtreeFlags上。

HostComponent

相较于其他节点,HostComponent节点比较特殊。

javascript
// 将当前 fiber 移出
popHostContext(workInProgress);
// 获取的是当前的 RootHostContainer
const rootContainerInstance = getRootHostContainer();
// 将当前 fiber 移出
popHostContext(workInProgress);
// 获取的是当前的 RootHostContainer
const rootContainerInstance = getRootHostContainer();

首先会移除当前的HostContext,其次是获取container节点。为什么能获取到真实节点呢?这是因为在beginWork中对于HostRootHostPortal节点都会将真实节点container全局存储到rootInstanceStackCursor.current,而在completeWork阶段将其移出。在访问子节点时就能正确获取到它所在的容器了。

真实节点可复用时

接下来就是渲染真实节点的过程:

javascript
if (current !== null && workInProgress.stateNode != null) {
 // 如果真实节点存在,那么进行更新
 updateHostComponent(
  current,
  workInProgress,
  type,
  newProps,
  rootContainerInstance,
 );

 if (current.ref !== workInProgress.ref) {
  markRef(workInProgress);
 }
}
if (current !== null && workInProgress.stateNode != null) {
 // 如果真实节点存在,那么进行更新
 updateHostComponent(
  current,
  workInProgress,
  type,
  newProps,
  rootContainerInstance,
 );

 if (current.ref !== workInProgress.ref) {
  markRef(workInProgress);
 }
}

如果老fiber存在且stateNode存在,说明已经复用了fiber且具备真实节点。这个时候只需要更新属性即可:

javascript
updateHostComponent = function (//...) {
  // ...
  
  // 类似结构:['name', '张三', 'id', 333, 'style', { color: 'red' }]
  const updatePayload = prepareUpdate(
    instance,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
    currentHostContext,
  );
  workInProgress.updateQueue = (updatePayload: any);
  // 标记为 Update
  if (updatePayload) {
    markUpdate(workInProgress);
  }
};
updateHostComponent = function (//...) {
  // ...
  
  // 类似结构:['name', '张三', 'id', 333, 'style', { color: 'red' }]
  const updatePayload = prepareUpdate(
    instance,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
    currentHostContext,
  );
  workInProgress.updateQueue = (updatePayload: any);
  // 标记为 Update
  if (updatePayload) {
    markUpdate(workInProgress);
  }
};

这个更新的过程正如UpdateQueue章节中提到的,主要是通过prepareUpdate方法对比节点的新旧props,最后将改变了的属性记录成数组形式。其中偶数index为键,奇数index为值。结构类似如下:

javascript
['name', '张三', 'id', 333, 'style', { color: 'red' }]
['name', '张三', 'id', 333, 'style', { color: 'red' }]

真实节点不可复用时

第二种情况是current不存在或者是stateNode不存在,那么真实节点就无法复用了,需要重新创建:

javascript
const instance = createInstance(
  type,
  newProps,
  rootContainerInstance,
  currentHostContext,
  workInProgress,
);

export function createInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): Instance {

  // 创建了 element
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );
  // 建立关系 node . '__reactFiber$' + randomKey = fiber
  precacheFiberNode(internalInstanceHandle, domElement);
  // 建立关系 node . '__reactProps$' + randomKey = props
  updateFiberProps(domElement, props);
  return domElement;
}
const instance = createInstance(
  type,
  newProps,
  rootContainerInstance,
  currentHostContext,
  workInProgress,
);

export function createInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): Instance {

  // 创建了 element
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );
  // 建立关系 node . '__reactFiber$' + randomKey = fiber
  precacheFiberNode(internalInstanceHandle, domElement);
  // 建立关系 node . '__reactProps$' + randomKey = props
  updateFiberProps(domElement, props);
  return domElement;
}

createInstance方法会创建一个真实节点,并且建立真实节点与fiberprops的关系。随后添加所有子节点:

javascript
appendAllChildren(instance, workInProgress, false, false);
// 如果是普通节点的话,会将 stateNode 存起来
// 与 fiber 建立了联系,真实节点存放在 stateNode 上
workInProgress.stateNode = instance
appendAllChildren(instance, workInProgress, false, false);
// 如果是普通节点的话,会将 stateNode 存起来
// 与 fiber 建立了联系,真实节点存放在 stateNode 上
workInProgress.stateNode = instance

appendAllChildren方法会将能渲染的子节点全部添加到当前创建的节点instance上,这样依次向上进行completeWork时,就会形成一棵具有真实节点树。