前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React源码解析之updateHostComponent和updateHostText

React源码解析之updateHostComponent和updateHostText

作者头像
进击的小进进
发布2020-02-24 12:33:54
1.1K0
发布2020-02-24 12:33:54
举报

前言:

还是在 React源码解析之workLoop 中,有一段HostComponentHostText的更新:

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

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

一、updateHostComponent

作用: 更新DOM标签

源码:

代码语言:javascript
复制
//更新 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()的作用是判断该节点是否是文本节点:

代码语言:javascript
复制
//判断是否是文本节点
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,里面内容也是字符串

关于dangerouslySetInnerHTML的介绍与使用,请参考: https://zh-hans.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

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

(4) 如果isDirectTextChildtrue,则表示其内部是文本,故直接渲染即可,nextChildren置为null,后面讲到的updateHostText()的源码也是类似的

(5) 如果之前节点不为空且为文本节点,但现在更新为其他类型的节点的话,则设一个ContentReset的标签

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

代码语言:javascript
复制
//标记 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 会发布到稳定版的新特性,对此模式感兴趣的同学,请参考: https://zh-hans.reactjs.org/docs/concurrent-mode-intro.html

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

二、updateHostText

作用: 更新 host 文本节点

源码:

代码语言:javascript
复制
//更新 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;
}

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


小进进还没开通留言功能,觉得不错的话,点「在看」、转发朋友圈都是一种支持 (●'◡'●)ノ

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 webchen 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 一、updateHostComponent
  • 二、updateHostText
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档