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 → 插入页面- React 调用组件的 render 方法
- render 返回虚拟 DOM 对象树
- React 把虚拟 DOM 转换为真实 DOM 元素
- 真实 DOM 被插入页面的相应位置
状态/属性更新
当 props 或 state 变化时:
setState/props 变化 → 重新调用 render() → 生成新虚拟 DOM → Diff 对比 → 更新真实 DOM- 状态或属性发生变化
- React 重新调用 render 方法
- 新 render 返回新的虚拟 DOM
- React 的 Diff 算法对比新旧虚拟 DOM
- 只更新变化的部分,而不是整个页面
三、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,只更新属性 |
| 列表使用 key | key 相同 | 复用节点;无 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 处理副作用,功能等同于 componentDidMount、componentDidUpdate、componentWillUnmount 的组合。
六、调和 (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 性能的关键。