Skip to content
目录

completeWork方法具体做了哪些事呢? 对于HostComponent和HostText(也就是dom节点和文字节点),执行创建节点更新节点等,对于其他的component, 比如 class Component ,pop context(因为在调和阶段push了相关的context)

completeWork部分代码

typescript
// 原生的DOM节点
 case HostComponent: {
      // 拿到的就是ReactDOM.render的第二个参数dom节点
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      if (current !== null && workInProgress.stateNode != null) {
        // 更新
        updateHostComponent(
          current,
          workInProgress,
          type,
          newProps,
          rootContainerInstance,
        );
        if (current.ref !== workInProgress.ref) {
          markRef(workInProgress);
        }
      } else {
        const currentHostContext = getHostContext();
        let wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {
          // ...
        } else {
          // 创建DOM节点
          let instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );
          // 构建DOM树
          appendAllChildren(instance, workInProgress, false, false);
          if (
            finalizeInitialChildren(
              instance,
              type,
              newProps,
              rootContainerInstance,
              currentHostContext,
            )
          ) {
            markUpdate(workInProgress);
          }
          workInProgress.stateNode = instance;
        }
      }
      break;
    }
    case HostText: {
      let newText = newProps;
      if (current && workInProgress.stateNode != null) {
        const oldText = current.memoizedProps;
        updateHostText(current, workInProgress, oldText, newText);
      } else {
        const rootContainerInstance = getRootHostContainer();
        const currentHostContext = getHostContext();
        let wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {
          // ...
        } else {
          workInProgress.stateNode = createTextInstance(
            newText,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );
        }
      }
      break;
    }

createInstance

typescript
export function createInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): Instance {
  let parentNamespace: string;
  if (__DEV__) {
    // ...
  } else {
    parentNamespace = ((hostContext: any): HostContextProd);
  }
  // 创建DOM节点 
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );
  // 将当前Fiber节点挂在到DOM节点上
  precacheFiberNode(internalInstanceHandle, domElement);
  // 将当前的props挂在到DOM节点上
  updateFiberProps(domElement, props);
  return domElement;
}
createElement

创建dom节点利用的document.createElement方法

typescript
export function createElement(
  type: string,
  props: Object,
  rootContainerElement: Element | Document,
  parentNamespace: string,
): Element {
  let isCustomComponentTag;
  // 当前的document对象
  const ownerDocument: Document = getOwnerDocumentFromRootContainer(
    rootContainerElement,
  );
  let domElement: Element;
  let namespaceURI = parentNamespace;
  if (namespaceURI === HTML_NAMESPACE) {
    namespaceURI = getIntrinsicNamespace(type);
  }
  if (namespaceURI === HTML_NAMESPACE) {
    if (type === 'script') {
      const div = ownerDocument.createElement('div');
      div.innerHTML = '<script><' + '/script>'; 
      const firstChild = ((div.firstChild: any): HTMLScriptElement);
      domElement = div.removeChild(firstChild);
    } else if (typeof props.is === 'string') {
      domElement = ownerDocument.createElement(type, {is: props.is});
    } else {
      domElement = ownerDocument.createElement(type);
      if (type === 'select') {
        const node = ((domElement: any): HTMLSelectElement);
        if (props.multiple) {
          node.multiple = true;
        } else if (props.size) {
          node.size = props.size;
        }
      }
    }
  } else {
    domElement = ownerDocument.createElementNS(namespaceURI, type);
  }
  return domElement;
}

appendAllChildren

当前方法做了什么呢?当走到这里的时候,说明当前节点的子节点的dom节点都创建了, 然后将子节点的dom节点都append到当前节点里,做一个DOM树的构建

typescript
appendAllChildren = function(
    parent: Instance,
    workInProgress: Fiber,
    needsVisibilityToggle: boolean,
    isHidden: boolean,
  ) {
    let node = workInProgress.child;
    while (node !== null) {
      if (node.tag === HostComponent || node.tag === HostText) {
        // 将子节点的dom节点appendChild到父节点里面 并不会对嵌套的child进行append操作
        // 只会append直接子节点
        appendInitialChild(parent, node.stateNode);
      } else if (node.tag === HostPortal) {
        // If we have a portal child, then we don't want to traverse
        // down its children. Instead, we'll get insertions from each child in
        // the portal directly.
      } else if (node.child !== null) {
        // 如果当前node不是HostComponent也不是HostText 并且存在child
        // 那么就去查找当前子节点 因为Fiber节点是存在比如class组件的
        // 但是DOM树需要挂载的是真实的DOM, 所以要去子节点里面找真实的DOM
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === workInProgress) {
        // 最后找回了自己,就可以跳出循环了
        return;
      }
      while (node.sibling === null) {
        // 如果当前节点的兄弟节点是不存在的,那么可以向上查找了
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
      node.sibling.return = node.return;
      // 在进去的兄弟节点
      node = node.sibling;
    }
    // 总结:这个方法的意思是什么呢?
    // 其实就是构建真实DOM的过程,将当前parent节点的直接真实的子DOM节点找到,
    // appendChild 构建DOM树
  };

finalizeInitialChildren

typescript
export function finalizeInitialChildren(
  domElement: Instance,
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
): boolean {
  // 初始化事件监听,以及child,style,children等属性
  setInitialProperties(domElement, type, props, rootContainerInstance);
  return shouldAutoFocusHostComponent(type, props);
}

setInitialProperties

当前方法代码量比较多,具体做了什么事呢 ?

  • 针对事件prop,做一个事件监听
  • 设置props接受的styles
  • 设置textContent(文本子节点)

updateHostComponent

更新DOM的方法

  • 如果前后dom接受的props没变, 就不需要做任何处理
  • 否则,需要针对dom的属性,包括styles做一个对比,然后变更
typescript
updateHostComponent = function(
    current: Fiber,
    workInProgress: Fiber,
    type: Type,
    newProps: Props,
    rootContainerInstance: Container,
  ) {
    const oldProps = current.memoizedProps;
    if (oldProps === newProps) {
      return;
    }
    const instance: Instance = workInProgress.stateNode;
    const currentHostContext = getHostContext();
    // 对比dom的属性的变化,其中包括styles等等 
    const updatePayload = prepareUpdate(
      instance,
      type,
      oldProps,
      newProps,
      rootContainerInstance,
      currentHostContext,
    );
    workInProgress.updateQueue = (updatePayload: any);
    if (updatePayload) {
      markUpdate(workInProgress);
    }
  };
prepareUpdate
typescript
export function prepareUpdate(
  domElement: Instance,
  type: string,
  oldProps: Props,
  newProps: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
): null | Array<mixed> {
  return diffProperties(
    domElement,
    type,
    oldProps,
    newProps,
    rootContainerInstance,
  );
}
diffProperties
  • 首先对比新老props
  • 收集变更的属性返回 然后将需要变动的属性添加到当前节点的Fiber的updateQueue上,在commit阶段再去做一个真实的变更操作

updateHostText

创建文本节点,对比文本是否一致,然后更新他

备案号: 浙ICP备2023000081号