人人都会AI编程

节点类型判断:元素节点、组件节点、文本节点的 Diff 逻辑

更新时间:2026-07-10

在 React 的协调(Reconciliation)过程中,Diff 算法需要根据新旧虚拟 DOM 节点的类型来决定如何处理。不同类型的节点,其比较和更新策略完全不同。React 将虚拟 DOM 节点主要分为三类:元素节点组件节点文本节点,在 Diff 时首先判断节点类型,再进入对应的处理分支。

1. 元素节点

元素节点对应原生 DOM 元素,如 <div><span><input> 等。在虚拟 DOM 对象中,它们的 type 字段是一个字符串(如 'div')。

判断方式:当新旧节点的 type 都是字符串且相等时,React 认为它们是同一类型的 DOM 元素,会复用底层 DOM 节点,仅更新变化的属性(props)和子节点。

如果 type 不同(例如从 <div> 变成 <span>),React 会认为整棵子树需要重建,直接销毁旧节点及其子节点,创建全新的 DOM 节点。

示例

// 从
<div className="old">Hello</div>
// 变为
<div className="new">World</div>

比较过程:

  • 发现都是 'div',类型一致,复用 DOM 节点。
  • 对比 props,更新 className 属性。
  • 对比子节点文本,从 'Hello' 更新为 'World'
// 从
<div>Hello</div>
// 变为
<span>Hello</span>

比较过程:

  • divspan 类型不同,直接卸载整个 <div> 及其子树,创建新的 <span> 并挂载。

2. 组件节点

组件节点对应自定义的 React 组件(函数组件或类组件)。在虚拟 DOM 对象中,type 字段是一个函数(函数组件)或类(类组件),而不是字符串。

判断方式:当新旧节点的 type 引用同一个组件构造函数(或函数)时,React 认为组件类型相同,会复用组件实例(类组件)或重新执行组件函数(函数组件)。如果组件类型不同,则卸载旧组件,挂载新组件。

组件节点的 Diff 过程是递归的:

  1. 确定组件类型后,更新组件的 props。
  2. 触发组件渲染,得到组件输出的子虚拟 DOM 树。
  3. 对子虚拟 DOM 树继续执行 Diff。

示例

// 旧树
<MyComponent name="Alice" />
// 新树
<MyComponent name="Bob" />

比较过程:

  • 两处 type 都指向同一个 MyComponent 函数,类型相同。
  • 更新 props 为 { name: 'Bob' },触发 MyComponent 重新渲染。
  • 对其内部返回的元素继续 Diff。

如果换了组件类型:

// 从
<MyComponent />
// 变为
<OtherComponent />

React 会彻底卸载 MyComponent(包括其使用到的 effects、state 等),并挂载新的 OtherComponent,整个子树重建。

3. 文本节点

文本节点对应虚拟 DOM 中的纯文本内容,其虚拟 DOM 对象不再有 type 字段,而是一个字符串或数字。

判断方式:当新旧节点都是文本节点(都是基本类型)时,React 直接比较字符串内容。如果内容相同,不做任何操作;如果内容不同,直接更新真实 DOM 的 textContent

如果一侧是文本节点,另一侧是元素节点或组件节点,则 React 会销毁旧节点,创建新类型的节点。例如从 Hello 变成 <span>Hello</span>,文本节点和元素节点类型不同,直接替换。

示例

// 旧树
<div>Hello</div>
// 新树
<div>World</div>

当处理 <div> 的子节点时:

  • 旧子节点为文本 'Hello',新子节点为文本 'World'
  • 两者都是文本节点,直接比较更新真实 DOM 的文本内容。

4. 节点类型判断的核心逻辑

React 在协调过程中的起点是 reconcileChildrenArrayreconcileSingleElement 等方法,它们首先获取 element.type 并进行判断:

function isSameType(newChild, oldFiber) {
  // 元素节点:typeof type === 'string'
  // 组件节点:typeof type === 'function'
  // 文本节点:无 type,通过对象结构区分
  if (typeof newChild === 'object' && newChild !== null) {
    return newChild.type === oldFiber.type;
  }
  // 文本节点
  return typeof newChild === 'string' || typeof newChild === 'number';
}

这种先判断类型再决定更新策略的方式,是 React Diff 算法高效的基础。理解它的行为有助于避免常见错误,比如:

  • 在列表中随意改变组件类型会导致子组件 完全销毁重建,丢失状态且产生不必要的性能开销。
  • 在 JSX 中混用文本和元素时,注意 React 如何处理相邻节点的 Diff。

掌握节点类型的判断逻辑,你就能更精准地预测 React 更新 DOM 的行为,并写出更高效的组件代码。