React源码解析之updateHostComponent和updateHostText

前言:
仍是在 React源码解析之workLoop 中,有一段HostComponentHostText的更新:javascript

  case HostComponent:
      //更新 DOM 标签
      return updateHostComponent(current, workInProgress, renderExpirationTime);
  case HostText:
      //更新文本节点
      return updateHostText(current, workInProgress);
复制代码

本文就解析下updateHostComponent()updateHostText()方法html

1、updateHostComponent
做用:
更新DOM标签java

源码:node

//更新 DOM 标签
function updateHostComponent(current, workInProgress, renderExpirationTime{
  //===暂时跳过 context
  pushHostContext(workInProgress);
  //判断可否复用服务端渲染的节点
  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }

  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  const prevProps = current !== null ? current.memoizedProps : null;

  let nextChildren = nextProps.children;
  //判断该节点是不是文本节点
  const isDirectTextChild = shouldSetTextContent(type, nextProps);
  //若是是文本节点的话(即里面再也不嵌套其余类型的节点)
  if (isDirectTextChild) {
    // We special case a direct text child of a host node. This is a common
    // case. We won't handle it as a reified child. We will instead handle
    // this in the host environment that also have access to this prop. That
    // avoids allocating another HostText fiber and traversing it.
    //没必要渲染子节点,直接显示其文本便可
    nextChildren = null;
  }
  //若是以前节点不为空且为文本节点,但如今更新为其余类型的节点的话
  else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
    // If we're switching from a direct text child to a normal child, or to
    // empty, we need to schedule the text content to be reset.
    //重置文本节点
    workInProgress.effectTag |= ContentReset;
  }
  //只有 HostComponent 和 ClassComponent 有使用该方法
  //由于只有这两个 Component 能拿到 DOM 实例
  markRef(current, workInProgress);

  // Check the host config to see if the children are offscreen/hidden.
  //若是该节点上设置了 hidden 属性,而且是异步渲染(ConcurrentMode)的话,那么它将最后更新

  //关于 ConcurrentMode 模式,请参考:https://zh-hans.reactjs.org/docs/concurrent-mode-intro.html
  if (
    workInProgress.mode & ConcurrentMode &&
    renderExpirationTime !== Never &&
    shouldDeprioritizeSubtree(type, nextProps)
  ) {
    if (enableSchedulerTracing) {
      markSpawnedWork(Never);
    }
    // Schedule this fiber to re-render at offscreen priority. Then bailout.
    //优先级最低,即最后更新
    workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
    return null;
  }
  //将 ReactElement 变成 fiber对象,并更新,生成对应 DOM 的实例,并挂载到真正的 DOM 节点上
  reconcileChildren(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  );
  return workInProgress.child;
}
复制代码

解析:
(1) context相关的之后讲
(2) tryToClaimNextHydratableInstance()方法的做用是判断可否复用服务端渲染的root内部已有的DOM节点
(3) shouldSetTextContent()的做用是判断该节点是不是文本节点:react

//判断是不是文本节点
export function shouldSetTextContent(type: string, props: Props): boolean {
  return (
    type === 'textarea' ||
    type === 'option' ||
    type === 'noscript' ||
    typeof props.children === 'string' ||
    typeof props.children === 'number' ||
    (typeof props.dangerouslySetInnerHTML === 'object' &&
      props.dangerouslySetInnerHTML !== null &&
      props.dangerouslySetInnerHTML.__html != null)
  );
}
复制代码

type应该表示html里的标签,如<textarea><option>noscript
props.children指节点里的内容是不是字符串仍是数字
dangerouslySetInnerHTMLinnerHTML,里面内容也是字符串web

关于dangerouslySetInnerHTML的介绍与使用,请参考:zh-hans.reactjs.org/docs/dom-el…app

也就是说,一旦shouldSetTextContent()判断为true,就肯定该节点为文本节点dom

(4) 若是isDirectTextChildtrue,则表示其内部是文本,故直接渲染便可,nextChildren置为null,后面讲到的updateHostText()的源码也是相似的异步

(5) 若是以前节点不为空且为文本节点,但如今更新为其余类型的节点的话,则设一个ContentReset的标签oop

(6) markRef的做用是标记ref
只有HostComponentClassComponent有使用该方法,由于只有这两个Component能直接获取到DOM实例的引用:

//标记 ref
function markRef(current: Fiber | null, workInProgress: Fiber{
  const ref = workInProgress.ref;
  if (
    (current === null && ref !== null) ||
    (current !== null && current.ref !== ref)
  ) {
    // Schedule a Ref effect
    workInProgress.effectTag |= Ref;
  }
}
复制代码

若是是第一次渲染而且设置了 ref 引用的话,或者不是第一次渲染,可是 ref 的引用发生变化的话,则设置Ref标签

(7) 若是设置了ConcurrentMode模式,而且渲染的优先级不是最低的Never的话,则将该节点的更新优先级重置为最低优先级Neverreturn null则表示不更新

ConcurrentMode模式,个人理解是异步渲染 UI(随时暂停,随时切换),应该是 React 17 会发布到稳定版的新特性,对此模式感兴趣的同窗,请参考:zh-hans.reactjs.org/docs/concur…

(8) 若是 (7) 条件不成立的话,则往下执行reconcileChildren(),将 ReactElement 变成 fiber对象,并更新,生成对应 DOM 的实例,并挂载到真正的 DOM 节点上
关于reconcileChildren()的讲解,请参考:React源码解析之FunctionComponent(上)

2、updateHostText
做用:
更新 host 文本节点

源码:

//更新 host 文本节点
function updateHostText(current, workInProgress{
  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }
  // Nothing to do here. This is terminal. We'll do the completion step
  // immediately after.
  //没有对 DOM 进行操做的地方,直接渲染出来便可
  return null;
}
复制代码

解析:
1、updateHostComponent中的(4)类似,文本节点直接渲染出来便可。


(完)

相关文章
相关标签/搜索