React16源码: React中的HostComponent & HostText的源码实现

HostComponent & HostText

1 )概述

  • HostComponent 就是我们dom原生的这些节点, 如: div, span, p 标签这种
    • 使用的是小写字母开头的这些节点一般都认为它是一个 HostComponent
  • HostText,它是单纯的文本节点
  • 主要关注它们的一个更新过程

2 )源码

定位到 packages/react-reconciler/src/ReactFiberBeginWork.js

进入 updateHostComponent 这个API

js 复制代码
function updateHostComponent(current, workInProgress, renderExpirationTime) {
  // 这个先跳过
  pushHostContext(workInProgress);

  if (current === null) {
    // 对于整个应用来讲,如果需要复用服务端渲染返回的dom内容,只有 HostComponent 和 HostText
    // 是需要被复用的,对于 class component 和 function component 本身是不对应于dom的某个节点的
    // 不会调用 hydrate 相关的东西
    tryToClaimNextHydratableInstance(workInProgress);
  }

  // 获取type和props, 对于 HostComponent 没有 state的概念
  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.
    // 之前props存在,并且也是文本,则重新设置 tag
    workInProgress.effectTag |= ContentReset;
  }

  // 在 class component 和 host component 都有这个
  // 在 class component 是有 instance
  // 在 dom 节点,也有 instance
  // 对应这两种节点才能拿到 ref, 否则没有引用
  markRef(current, workInProgress);

  // Check the host config to see if the children are offscreen/hidden.
  // 符合 下面这种情况,其中 当前的更新的优先级不为 Never, workInProgress.mode 要符合 ConcurrentMode 并且 设置了 hidden
  // 比如模拟自定义的滑动,浏览器的滚动条,通过这个属性来进行一个优化,把不需要显示的组件设置为 hidden
  // 这样每次滑动,就不需要更新这个组件,减少损耗性能
  if (
    renderExpirationTime !== Never &&
    workInProgress.mode & ConcurrentMode &&
    shouldDeprioritizeSubtree(type, nextProps)
  ) {
    // Schedule this fiber to re-render at offscreen priority. Then bailout.
    workInProgress.expirationTime = Never; // 设置成 Never 这个节点将永不会更新到
    return null;
  }

  // 调用来创建,调和子节点
  reconcileChildren(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  );
  return workInProgress.child;
}
  • 进入 shouldSetTextContent

    • 来自于 packages/react-reconciler/src/ReactFiberHostConfig.js 这里打包对应的是

      • 这里根据不同平台指向不同的js
    • packages/react-reconciler/src/forks/ReactFiberHostConfig.dom.js 看到

      js 复制代码
      export * from 'react-dom/src/client/ReactDOMHostConfig';
    • 定位到 react-dom/src/client/ReactDOMHostConfig.js 中

      js 复制代码
      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)
        );
      }
      • 基于此来确定是否继续调和子节点
      • 因为, textarea, option, noscript 这种内部只能显示字符串,里面放新节点没有任何意义
      • string和number是在一个dom节点内的内容
      • 如果有 dangerouslySetInnerHTML 相关也是一个特殊情况
  • 进入 shouldDeprioritizeSubtree

    js 复制代码
    // packages/react-dom/src/client/ReactDOMHostConfig.js
    export function shouldDeprioritizeSubtree(type: string, props: Props): boolean {
      return !!props.hidden;
    }

定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L733

进入 updateHostText 这个API

js 复制代码
function updateHostText(current, workInProgress) {
  // 跳过 hydrate 过程
  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }
  // Nothing to do here. This is terminal. We'll do the completion step
  // immediately after.
  return null;
}
  • 对于 HostText 来说,不可能有子节点的,不需要调用 reconcileChildren
  • 真正被插入dom里面要等到后期完成树渲染进行commit时才会放进去
  • 在 update 的过程中,不涉及dom操作, 在completeUnitOfWork 时才会去更新dom
相关推荐
Canace6 分钟前
基于 Codex Agent Harness 套壳实现自己的 AI 产品:Agent 运行时与任务编排实践
前端·人工智能·agent
星栈独行32 分钟前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·人工智能·ui·ux
hunterandroid36 分钟前
[Android 从零到一] APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅
android·前端
Coodor1 小时前
在前端如何转换IC卡卡号格式
前端·javascript·nfc·卡号格式
invicinble1 小时前
数字系统--c端数字环境入口和操作系统
前端
hunterandroid1 小时前
[鸿蒙从零到一] relationalStore 高级实战:事务、版本迁移与数据库加密
前端
百慕大三角1 小时前
给 SPA 加页面切换动画:View Transitions API 从入门到实战
前端·javascript
不可能片场1 小时前
自动化也会丢:一次 WorkBuddy 自动化清空的复盘
前端·electron
求道於盲1 小时前
异步编程中的 Future
前端
Z兽兽1 小时前
HBuilder打包web地址的apk,更新dist后如何自动清缓存
前端·android应用打包