FumadocsZDecode
React

React render 函数的原理

render 函数是 React 组件的核心方法,负责描述组件的 UI。React 根据 render 返回的虚拟 DOM,通过 Diff 和最小化 DOM 操作来实现高效更新。


一、render 函数的作用

render 函数有两个核心职责:

1. 描述 UI

render 返回一个描述"页面应该长什么样"的对象——虚拟 DOM。它不直接操作真实 DOM,而是让 React 负责转换和更新。

class Button extends React.Component {
  render() {
    return <button onClick={this.props.onClick}>{this.props.label}</button>
  }
}

2. 保持纯净

render 必须是纯函数——不能产生副作用,同样的 props 和 state 总是返回同样的结果。副作用应该放在生命周期方法中。

// ✗ 错误:在 render 中修改 state
class Bad extends React.Component {
  render() {
    this.state.count++  // 禁止
    return <div>{this.state.count}</div>
  }
}

// ✓ 正确:在 componentDidMount 处理副作用
class Good extends React.Component {
  componentDidMount() {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    return <div>{this.state.count}</div>
  }
}

二、render 执行的三个阶段

初次渲染

调用 render() → 生成虚拟 DOM 树 → 转换为真实 DOM → 插入页面
  1. React 调用组件的 render 方法
  2. render 返回虚拟 DOM 对象树
  3. React 把虚拟 DOM 转换为真实 DOM 元素
  4. 真实 DOM 被插入页面的相应位置

状态/属性更新

当 props 或 state 变化时:

setState/props 变化 → 重新调用 render() → 生成新虚拟 DOM → Diff 对比 → 更新真实 DOM
  1. 状态或属性发生变化
  2. React 重新调用 render 方法
  3. 新 render 返回新的虚拟 DOM
  4. React 的 Diff 算法对比新旧虚拟 DOM
  5. 只更新变化的部分,而不是整个页面

三、Diff 算法——如何找到变化

React 使用 Diffing 算法对比新旧虚拟 DOM,找出最小的变更集:

// 初次 render:虚拟 DOM 结构
{
  type: 'div',
  props: { className: 'user', children: [{ type: 'span', props: { children: 'John' } }] }
}

// 更新后:只有内容变了
{
  type: 'div',
  props: { className: 'user', children: [{ type: 'span', props: { children: 'Jane' } }] }
}

// Diff 结果:只更新 span 内的文本,div 复用

Diff 的三条规则:

规则例子React 行为
不同类型节点<div><p>销毁旧节点,创建新节点
相同类型节点<div class="a"><div class="b">复用 DOM,只更新属性
列表使用 keykey 相同复用节点;无 key 则按位置 diff

四、虚拟 DOM 的优势

为什么不直接操作真实 DOM?

// 直接操作真实 DOM(低效)
document.getElementById('count').innerHTML = '5'
document.getElementById('count').style.color = 'red'
document.getElementById('count').classList.add('highlight')

// React 方式(高效)
// 计算差异(Diff)→ 批量更新 → 一次性操作 DOM
  • 性能 — 每次 DOM 操作都很昂贵(触发重排/重绘)。React 在内存中对比,减少实际 DOM 操作次数
  • 一致性 — Virtual DOM 是状态的完整镜像,避免界面与数据不同步
  • 批量更新 — 多个 setState 被合并成一次 render 和 DOM 更新

五、类组件 vs 函数组件

类组件:必须实现 render 方法

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { count: 0 }
  }

  render() {
    return <button onClick={() => this.setState({ count: this.state.count + 1 })}>{this.state.count}</button>
  }
}

函数组件:函数体本身就是"render"

function Counter() {
  const [count, setCount] = React.useState(0)
  
  // 函数直接返回 JSX,无需显式 render 方法
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

函数组件通过 useEffect Hook 处理副作用,功能等同于 componentDidMountcomponentDidUpdatecomponentWillUnmount 的组合。


六、调和 (Reconciliation)

调和是 React 应用 Diff 算法更新组件的完整过程:

新状态产生 

React 调用 render 获取新虚拟 DOM

Diff 新旧虚拟 DOM 树

找出需要更新的部分(Effect List)

最小化更新真实 DOM

执行生命周期方法(如 componentDidUpdate)

页面更新完成

关键点:调和确保了 React 只修改必要的部分,避免全量重绘,大幅提升性能。


七、性能优化建议

避免在 render 中计算复杂操作

// ✗ 每次 render 都计算一遍(低效)
render() {
  const filtered = this.props.items.filter(item => item.active)
  return <List items={filtered} />
}

// ✓ 用 useMemo 缓存计算结果(高效)
const filtered = React.useMemo(
  () => items.filter(item => item.active),
  [items]
)

使用 React.memo 避免不必要的重渲染

const UserCard = React.memo(({ user }: { user: User }) => {
  // props 不变就不重新渲染
  return <div>{user.name}</div>
})

使用 key 帮助 Diff 识别列表项

// ✗ 使用 index 作 key(当列表排序/过滤时会出错)
{items.map((item, index) => <li key={index}>{item.name}</li>)}

// ✓ 使用稳定的 ID(推荐)
{items.map(item => <li key={item.id}>{item.name}</li>)}

核心机制:render 函数描述 UI,React 据此生成虚拟 DOM。当状态变化时,render 被重新调用,新虚拟 DOM 通过 Diff 与旧虚拟 DOM 对比,找出最小变更集,最终以最低成本更新真实 DOM。这个过程(调和)是 React 性能的关键。

On this page