专栏首页前端干货和生活感悟React源码解析之updateHostComponent和updateHostText

React源码解析之updateHostComponent和updateHostText

前言:

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

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

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

一、updateHostComponent

作用: 更新DOM标签

源码:

//更新 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()的作用是判断该节点是否是文本节点:

//判断是否是文本节点
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实例的引用:

//标记 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 文本节点

源码:

//更新 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)相似,文本节点直接渲染出来即可。


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

本文分享自微信公众号 - webchen(webchen1995),作者:webchen

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React源码解析之FunctionComponent(上)

    在 React源码解析之workLoop 中讲到当workInProgress.tag为FunctionComponent时,会进行FunctionCompon...

    进击的小进进
  • React源码解析之workLoop

    在React源码解析之renderRoot概览中,提到了renderRoot()会调用 workLoop()/workLoopSync() 进行循环单元的更新:

    进击的小进进
  • React源码解析之completeUnitOfWork

    (1) 关于completeUnitOfWork()在哪里使用到,请看下 React源码解析之workLoop 中的二、performUnitOfWork

    进击的小进进
  • 2016 年人工智能最重要的发展:面向所有人的深度学习

    【新智元导读】过去一年人工智能和深度学习最重要的发展不在技术,而是商业模式的转变。过去6个月,所有巨头都将自己的深度学习IP开源。Data Science Ce...

    新智元
  • 2016 年人工智能最重要的发展:面向所有人的深度学习

    过去一年人工智能和深度学习最重要的发展不在技术,而是商业模式的转变。过去6个月,所有巨头都将自己的深度学习IP开源。Data Science Central 网...

    华章科技
  • 仓储管理怎样从三方物流中降低成本

    咱们先从场地费用这个角度来探讨下,仓储管理中,其场地费用通常包括:仓库租金、水费、电费、物业费、采暖费(部分客户的产品对温度有要求,尤其是冬季需要采暖)、制冷费...

    用户6848238
  • 聊聊java中的哪些Map:(二)HashMap中的TreeNode

    TreeNode则是hashMap树化之后,组成树的基本节点。需要注意的是,TreeNode继承了LiknedHashMap.Entry ,LinkedHas...

    冬天里的懒猫
  • Kotlin Contract

    因为编译器在处理s.length时,会将 s 推断成value-parameter s: String? = ...并不是 String 类型。智能推断失效了,...

    fengzhizi715
  • iOS接入移动直播SDK 播放背景音乐失败

    withBeginNotify:(void (^)(NSInteger errCode))beginNotify

    用户5933559
  • 【2019年8月版】OCP 071认证考试原题-第36题

    Which three statements are true about sequences in a single instance Orade datab...

    用户5892232

扫码关注云+社区

领取腾讯云代金券