第二章:React Render 实现原理

134 阅读2分钟

本章节将深入探讨 React 中 render 函数的实现原理,这是理解 React 如何将虚拟 DOM 转换为真实 DOM 的关键。

什么是 Render?

Render 是 React 中负责将虚拟 DOM 转换为真实 DOM 的核心函数。它接收两个参数:

  1. element: 要渲染的虚拟 DOM 元素
  2. container: 目标容器 DOM 节点

实现原理

让我们通过一个简单的示例来了解其工作原理:

1. 基础设置

首先,我们需要定义一些基础常量和函数:

const TEXT_ELEMENT_TYPE = "TEXT_ELEMENT";

// 创建虚拟 DOM 元素
function createElement(type, props, ...children) {
    return {
        type,
        props: {
            ...props,
            children: children.map(child =>
                typeof child === "object" ? child : createTextElement(child)
            ),
        },
    };
}

// 创建文本节点
function createTextElement(text) {
    return {
        type: TEXT_ELEMENT_TYPE,
        props: {
            nodeValue: text,
            children: [],
        },
    };
}

2. Render 函数实现

function render(element, container) {
    // 1. 创建 DOM 节点
    const dom = element.type === TEXT_ELEMENT_TYPE
        ? document.createTextNode("")  // 文本节点
        : document.createElement(element.type);  // 普通节点

    // 2. 处理非 children 属性
    Object.keys(element.props)
        .filter(key => key !== "children")
        .forEach(key => {
            dom[key] = element.props[key];
        });

    // 3. 递归处理子节点
    element.props.children.forEach(child => 
        render(child, dom)
    );

    // 4. 将节点添加到容器
    container.appendChild(dom);
}

3. 使用示例

const App = (
    <div>
        <h1>你好,React!</h1>
        <p>这是一个使用 CDN 引入 React 的示例。</p>
    </div>
);

const root = document.getElementById("root");
React.render(App, root);

实现细节解析

1. 节点创建

Render 函数首先需要根据元素类型创建对应的 DOM 节点:

  • 如果是文本节点(TEXT_ELEMENT_TYPE),使用 document.createTextNode()
  • 如果是普通节点,使用 document.createElement()

2. 属性处理

对于非 children 的属性,直接将其赋值给 DOM 节点:

Object.keys(element.props)
    .filter(key => key !== "children")
    .forEach(key => {
        dom[key] = element.props[key];
    });

3. 子节点处理

通过递归调用 render 函数处理所有子节点:

element.props.children.forEach(child => 
    render(child, dom)
);

4. 节点挂载

最后将创建好的 DOM 节点添加到容器中:

container.appendChild(dom);

5. 整体代码

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React CDN 示例</title>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      const TEXT_ELEMENT_TYPE = "TEXT_ELEMENT";
      function createElement(type, props, ...children) {
        return {
          type,
          props: {
            ...props,
            children: children.map((child) =>
              typeof child === "object" ? child : createTextElement(child)
            ),
          },
        };
      }

      function createTextElement(text) {
        return {
          type: TEXT_ELEMENT_TYPE,
          props: {
            nodeValue: text,
            children: [],
          },
        };
      }

      function render(element, container) {
        // 针对文字节点和普通节点进行分别处理
        const dom =
          element.type === TEXT_ELEMENT_TYPE
            ? document.createTextNode("")
            : document.createElement(element.type);

        // 针对非children进行处理
        Object.keys(element.props)
          .filter((key) => key !== "children")
          .forEach((key) => {
            dom[key] = element.props[key];
          });
        // 针对children进行处理
        element.props.children.forEach((child) => render(child, dom));
        // 添加dom到容器
        container.appendChild(dom);
      }
      React = {
        createElement,
        render,
      };
    </script>

    <script type="text/babel">
      const App = (
        <div>
          <h1>你好,React!</h1>
          <p>这是一个使用CDN引入React的示例。</p>
        </div>
      );
      console.log(App);

      const root = document.getElementById("root");
      React.render(App, root);
    </script>
  </body>
</html>

关键点总结

  1. Render 函数是递归的,它会深度优先遍历整个虚拟 DOM 树
  2. 每个节点都会被转换为对应的真实 DOM 节点
  3. 文本节点需要特殊处理,使用 TEXT_ELEMENT_TYPE 标识
  4. 属性处理时需要注意区分 children 和其他属性
  5. 整个过程是同步的,会一次性完成整个 DOM 树的构建

下一步

在下一章节中,我们将探讨如何优化 render 过程,实现Concurrent Mode,以提高渲染性能。