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
相关推荐
2401_8791036842 分钟前
24.11.10 css
前端·css
ComPDFKit2 小时前
使用 PDF API 合并 PDF 文件
前端·javascript·macos
yqcoder2 小时前
react 中 memo 模块作用
前端·javascript·react.js
优雅永不过时·3 小时前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠5 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱5 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号6 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72936 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲6 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
王解6 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6