实现 React 多个原生标签子节点渲染

基于文章 简易实现 React 页面初次渲染

本文将实现多个原生标签子节点渲染,主要涉及 render 阶段的修改。

js 复制代码
// 多个原生标签子节点
const jsx = (
  <div className="box border">
    <h1 className="border">omg</h1>
    <h2>react</h2>
  </div>
)

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(jsx);

BeginWork 阶段

reconcileChildFibers 函数增加子节点是数组的判断。

js 复制代码
// 协调子节点
function createChildReconciler(shouldTrackSideEffects: boolean) {
  function reconcileChildFibers(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    newChild: any
  ) {
    // 检查 newChild 类型,单个节点
    if (typeof newChild === "object" && newChild !== null) {
      switch (newChild.$$typeof) {
        case REACT_ELEMENT_TYPE: {
          return placeSingleChild(
            reconcileSingleElement(returnFiber, currentFirstChild, newChild)
          );
        }
      }
    }
    
    // 子节点是数组
    if (isArray(newChild)) {
      return reconcileChildrenArray(returnFiber, currentFirstChild, newChild);
    }

    return null;
  }

  return reconcileChildFibers;
}

reconcileChildrenArray 函数协调节点数组。

js 复制代码
// 根据 TypeAndProps 创建fiber
export function createFiberFromTypeAndProps(
  type: any,
  key: null | string,
  pendingProps: any
) {
  let fiberTag: WorkTag = IndeterminateComponent;

  if (isStr(type)) {
    // 原生标签
    fiberTag = HostComponent;
  }

  const fiber = createFiber(fiberTag, pendingProps, key);
  fiber.elementType = type;
  fiber.type = type;
  return fiber;
}

// 根据 ReactElement 创建Fiber
export function createFiberFromElement(element: ReactElement) {
  const { type, key } = element;
  const pendingProps = element.props;
  const fiber = createFiberFromTypeAndProps(type, key, pendingProps);
  return fiber;
}

function createChild(returnFiber: Fiber, newChild: any): Fiber | null {
    if (typeof newChild === "object" && newChild !== null) {
      switch (newChild.$$typeof) {
        case REACT_ELEMENT_TYPE: {
          const created = createFiberFromElement(newChild);
          created.return = returnFiber;
          return created;
        }
      }
    }

    return null;
}
  
function reconcileChildrenArray(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    newChildren: Array<any>
  ) {
    let resultFirstChild: Fiber | null = null; // 头结点
    let previousNewFiber: Fiber | null = null;
    let oldFiber = currentFirstChild;
    let newIdx = 0; // 会在后面多个 for 循环中使用,所以没有定义到 for 循环语句中

    // 初次渲染没有 oldFiber
    if (oldFiber === null) {
      for (; newIdx < newChildren.length; newIdx++) {
        const newFiber = createChild(returnFiber, newChildren[newIdx]);
        
        // null 在 react 中不渲染
        if (newFiber === null) {
          continue;
        }

        // 组件更新阶段,判断在更新前后的位置是否一致,如果不一致,需要移动
        // 和 Vue 不同,react 存储使用的是链表,必须记录 index
        // Vue 存储使用的是数组,天然带下标,不需要记录 index
        newFiber.index = newIdx

        // 第一个节点,不要用 newIdx 判断,因为有可能有 null,而 null 不是有效的 fiber 
        if (previousNewFiber === null) {
          previousNewFiber = newFiber
        } else {
          previousNewFiber.sibling = newFiber
        }
        previousNewFiber = newFiber
      }

      return resultFirstChild
    }

    return resultFirstChild
  }

CompleteWork 阶段

有了兄弟节点,需要遍历。

js 复制代码
function appendAllChildren(parent: Element, workInProgress: Fiber) {
  let nodeFiber = workInProgress.child; // 链表结构
  while (nodeFiber !== null) {
    parent.appendChild(nodeFiber.stateNode); // nodeFiber.stateNode是DOM节点
    nodeFiber = nodeFiber.sibling;
  }
}
相关推荐
前端不太难3 小时前
从 Navigation State 反推架构腐化
前端·架构·react
前端程序猿之路4 小时前
Next.js 入门指南 - 从 Vue 角度的理解
前端·vue.js·语言模型·ai编程·入门·next.js·deepseek
大布布将军4 小时前
⚡️ 深入数据之海:SQL 基础与 ORM 的应用
前端·数据库·经验分享·sql·程序人生·面试·改行学it
川贝枇杷膏cbppg4 小时前
Redis 的 RDB 持久化
前端·redis·bootstrap
JIngJaneIL5 小时前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
天外天-亮5 小时前
v-if、v-show、display: none、visibility: hidden区别
前端·javascript·html
jump_jump5 小时前
手写一个 Askama 模板压缩工具
前端·性能优化·rust
be or not to be5 小时前
HTML入门系列:从图片到表单,再到音视频的完整实践
前端·html·音视频
90后的晨仔6 小时前
在macOS上无缝整合:为Claude Code配置魔搭社区免费API完全指南
前端
沿着路走到底6 小时前
JS事件循环
java·前端·javascript