Skip to content

HostComponent 组件

元素类型的节点

对于 typeof type === 'string' 类型的节点,其作为元素类型的节点进行处理,这种类型的节点相对比较简单,主要是以下几个步骤

  1. namespace 命名空间的管理

其通过 堆栈 的方式去管理节点树中特殊命名空间的子节点树,如 <svg> xxxx </svg>

js
/**
 * 通过 栈的方式 维护当前节点下子节点 对应的 namespace
 * @param {*} fiber
 * @returns
 */
function pushHostContext(fiber: Fiber): void {
  // 获取根节点的实例对象
  const rootInstance: Container = requiredContext(
    rootInstanceStackCursor.current
  );
  // 获取当前的命名空间
  const context: HostContext = requiredContext(contextStackCursor.current);
  // 获取当前元素类型对应的命名空间 ( svg => svg namespace)
  const nextContext = getChildHostContext(context, fiber.type, rootInstance);

  // Don't push this Fiber's context unless it's unique.
  // 如果命名空间没有切换 那就直接返回
  if (context === nextContext) {
    return;
  }

  // Track the context and the Fiber that provided it.
  // This enables us to pop only Fibers that provide unique contexts.
  push(contextFiberStackCursor, fiber, fiber);
  push(contextStackCursor, nextContext, fiber);
}

先通过 requiredContext(contextStackCursor.current);获取堆栈中最上面的命名空间上下文('http://www.w3.org/1999/xhtml') , 然后根据 getChildHostContext(context, fiber.type, rootInstance)获取当前子孙组件的命名空间类型,如果相同就不处理,如果不同就将新的命名空间 push 到堆栈中,其中获取子孙节点的命名空间的方式由不同的客户端进行个性化定义,如在浏览器端就是 react-dom 中定义的,具体如下:

js
/**
 * 根据父命名空间和元素类型,确定子元素应该使用的命名空间。
 * 在处理 HTML、SVG 和 MathML 混合的文档时,正确的命名空间非常重要,
 * 因为不同的命名空间有不同的元素和属性定义
 * @param {*} parentNamespace
 * @param {*} type
 * @returns ''http://www.w3.org/1999/xhtml''
 */
export function getChildNamespace(
  parentNamespace: string | null,
  type: string
): string {
  // 默认是 html namespace
  if (parentNamespace == null || parentNamespace === HTML_NAMESPACE) {
    // No (or default) parent namespace: potential entry point.
    return getIntrinsicNamespace(type);
  }
  if (parentNamespace === SVG_NAMESPACE && type === "foreignObject") {
    // We're leaving SVG.
    return HTML_NAMESPACE;
  }
  // By default, pass namespace below.
  return parentNamespace;
}

最后在 compeleteUnitOfWork 的时候在 popHostContext 退出当前命名空间

  1. 对于特殊的 文本类型的节点的处理
js
/**
 * 判断节点的子节点是否需要作为 文本内容 插入
 *  如 <textarea> </textarea>  <div>12312</div>
 * @param {*} type
 * @param {*} props
 * @returns
 */
export function shouldSetTextContent(type: string, props: Props): boolean {
  return (
    type === "textarea" ||
    type === "noscript" ||
    typeof props.children === "string" ||
    typeof props.children === "number" ||
    (typeof props.dangerouslySetInnerHTML === "object" &&
      props.dangerouslySetInnerHTML !== null &&
      props.dangerouslySetInnerHTML.__html != null)
  );
}

跟命名空间的判断一样,其也在各自的客户端进行定义,对于浏览器端,主要是处理 textarea 、 noscript 等子节点不是通过 文本,而是通过 innerText 方式去赋值处理

  1. 调和子节点

reconcileChildren(current, workInProgress, nextChildren, renderLanes);