React源码中的dom-diff

这一章就来讲讲React在协调阶段的beginWork里面主要做的事情 – dom diff

本文主要讲的是React17.0.2版本的diff,在此我也画了一个简单的流程图:

React源码中的dom-diff_第1张图片

reconcileChildren

dom diff的入口函数就是reconcileChildren,那么他的源码如下:

//packages/react-reconciler/src/ReactFiberBeginWork.old.js
export function reconcileChildren(
  current: Fiber | null,//当前的fiber节点
  workInProgress: Fiber,// 新生成的fiber
  nextChildren: any,//  新生成的reactElement内容
  renderLanes: Lanes,//渲染优先级
) {
   
  if (current === null) {
   
    // 如果没有已经渲染的fiber树,则直接把reactElement内容渲染上去
    // If this is a fresh new component that hasn't been rendered yet, we
    // won't update its child set by applying minimal side-effects. Instead,
    // we will add them all to the child before it gets rendered. That means
    // we can optimize this reconciliation pass by not tracking side-effects.
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderLanes,
    );
  } else {
   
    // If the current child is the same as the work in progress, it means that
    // we haven't yet started any work on these children. Therefore, we use
    // the clone algorithm to create a copy of all the current children.

    // If we had any progressed work already, that is invalid at this point so
    // let's throw it out.
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      current.child,
      nextChildren,
      renderLanes,
    );
  }
}

reconcileChildren的源码并不长,主要做了两件事

  • 如果是首次渲染,则会把已经处理好的fiber树进行挂载。
  • 如果不是首次渲染则调用reconcileChildFibers进行下一步处理。

我们关注一下mountChildFibersreconcileChildFibers,我们发现这两个函数分别指向ChildReconciler,只是mountChildFibers的参数为falsereconcileChildFibers的参数为true。我们在这里先埋下一个点,看看这个参数对后期的流程有什么影响。

React源码中的dom-diff_第2张图片

我们继续深入可以发现,ChildReconciler这个函数冰的执行返回了reconcileChildFibers,所以这便是reconcileChildren的核心功能代码所在了。

React源码中的dom-diff_第3张图片

reconcileChildFibers

 function reconcileChildFibers(
    returnFiber: Fiber,    currentFirstChild: Fiber | null,    newChild: any,    lanes: Lanes,  ): Fiber | null {
   
    // This function is not recursive.
    // If the top level item is an array, we treat it as a set of children,
    // not as a fragment. Nested arrays on the other hand will be treated as
    // fragment nodes. Recursion happens at the normal flow.

    // Handle top level unkeyed fragments as if they were arrays.
    // This leads to an ambiguity between <>{[...]} and <>....
    // We treat the ambiguous cases above the same.
    const isUnkeyedTopLevelFragment =
      typeof newChild === 'object' &&
      newChild !== null &&
      newChild.type === REACT_FRAGMENT_TYPE &&
      newChild.key === null;
    if (isUnkeyedTopLevelFragment) {
   
      newChild = newChild.props.children;
    }

    // Handle object types
    const isObject = typeof newChild === 'object' && newChild !== null;

    // 处理对象类型
    if (isObject) {
   
      switch (newChild.$$typeof) {
   
        // REACT_ELEMENT_TYPE类型
        case REACT_ELEMENT_TYPE:
          return placeSingleChild(
            reconcileSingleElement(
              returnFiber,
              currentFirstChild,
              newChild,
              lanes,
            ),
          );
        // REACT_PORTAL_TYPE类型  
        case REACT_PORTAL_TYPE:
          return placeSingleChild(
            reconcileSinglePortal(
              returnFiber,
              currentFirstChild,
              newChild,
              lanes,
            ),
          );
        // REACT_LAZY_TYPE类型    
        case REACT_LAZY_TYPE:
          if (enableLazyElements) {
   
            const payload = newChild._payload;
            const init = newChild._init;
            // TODO: This function is supposed to be non-recursive.
            return reconcileChildFibers(
              returnFiber,
              currentFirstChild,
              init(payload),
              lanes,
            );
          }
      }
    }

    // 字符串与数字类型
    if (typeof newChild === 'string' || typeof newChild === 'number') {
   
      return placeSingleChild(
        reconcileSingleTextNode(
          returnFiber,
          currentFirstChild,
          '' + newChild,
          lanes,
        ),
      );
    }
    // 数组类型 
    if (isArray(newChild)) {
   
      return reconcileChildrenArray(
        returnFiber,
        currentFirstChild,
        newChild,
        lanes,
      );
    }

    // 可迭代的类型
    if (getIteratorFn(newChild)) {
   
      return reconcileChildrenIterator(
        returnFiber,
        currentFirstChild,
        newChild,
        lanes,
      );
    }

    if (isObject) {
   
      throwOnInvalidObjectType(returnFiber, newChild);
    }

    if (__DEV__) {
   
      if (typeof newChild === 'function') {
   
        warnOnFunctionType(returnFiber);
      }
    }
    if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
   
      // If the new child is undefined, and the return fiber is a composite
      // component, throw an error. If Fiber return types are disabled,
      // we already threw above.
      switch (returnFiber.tag) {
   
        case ClassComponent: {
   
          if (__DEV__) {
   
            const instance = returnFiber.stateNode;
            if (instance.render._isMockFunction) {
   
              // We allow auto-mocks to proceed as if they're returning null.
              break;
            }
          }
        }
        // Intentionally fall through to the next case, which handles both
        // functions and classes
        // eslint-disable-next-lined no-fallthrough
        case Block:
        case FunctionComponent:
        case ForwardRef:
        case SimpleMemoComponent: {
   
          invariant(
            false,
            '%s(...): Nothing was returned from render. This usually means a ' +
              'return statement is missing. Or, to render nothing, ' +
              'return null.',
            getComponentName(returnFiber.type) || 'Component',
          

你可能感兴趣的:(reactjs)