React 面试官常问的一道灵魂题:React 是怎么更新 UI 的?它怎么知道页面哪个地方变了?
你以为这是考你 setState?
不,这其实是在考你 —— React 的 diff 算法理解是否扎实。
👦 前言:我是如何在项目中被“刷新页面”坑过的
还记得刚学 React 那会儿,只要一改数据,整个页面都重新渲染。
“你点个按钮干嘛整个页面都重新加载了啊?”
后来我才知道,没理解 Virtual DOM 和 diff 算法,就是 React 的“盲盒开发” ,啥都靠猜。
直到我认真啃完了 diff 原理,才明白:
“原来 React 更新页面,是在偷偷找不同,做一场精准手术!”
🎯 React diff 是什么?
React diff(差异算法) ,是 React 中用来比较新旧 Virtual DOM 树的机制,目标是:
🧠 找出最少的变化,做最少的 DOM 操作。
所以,每当你 setState 或 useState 更新数据,React 会:
- 生成一份新的虚拟 DOM;
- 把新旧虚拟 DOM 对比(diff) ;
- 根据变化,精准更新 DOM(而不是全部重绘)!
🔍 举个 React diff 的经典例子:
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
用户操作后:
<ul>
<li>A</li>
<li>X</li>
<li>C</li>
</ul>
你以为 React 会重新渲染整个 <ul>?其实不会。
如果你写得像这样👇(注意 key):
{ list.map(item => <li key={item.id}>{item.name}</li>) }
React 会聪明地只做:
B➜X:更新这个<li>- 其他不变,全部复用!
🧠 React diff 到底做了什么?
React diff 不是传统的全量比对算法(那样太慢),它做了三条性能换正确性的妥协:
🔹 1. 只对比同层级的节点
<div>
<span>A</span>
</div>
换成:
<div>
<p>B</p>
</div>
React 只比较 <span> 和 <p>,不会跨层级深度比对(这样速度更快)。
🔹 2. 节点类型不同 ➜ 直接替换
如果新旧节点的类型不同(比如 <span> ➜ <p>),React 会直接删掉原来的,再创建新的,不做复用。
🔹 3. 使用 key 提高识别准确性
最最重要的一条:React diff 依赖 key!
你这样写是对的:
todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))
如果你写错了👇:
todos.map((todo, index) => (
<li key={index}>{todo.title}</li>
))
就容易在增删中间项时,导致组件状态错乱(比如输入框值跳了位置、焦点丢失)。
🧪 小测验:下面这段代码的 key 有什么问题?
<ul>
{
items.map((item, index) => (
<li key={index}>{item.name}</li>
))
}
</ul>
答案:用 index 作为 key,不稳定,当列表发生“插入、删除”时,React 会错误判断元素变化,导致性能下降甚至 UI 闪烁、状态丢失。
小实验:手动触发 diff 流程
function App() {
const [list, setList] = useState([
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 3, name: 'C' }
])
return (
<div>
<button onClick={() => {
setList([
{ id: 1, name: 'A' },
{ id: 4, name: 'X' },
{ id: 3, name: 'C' }
])
}}>
替换 B ➜ X
</button>
<ul>
{list.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
)
}
分析:
- 按钮点击后,
B变成X,但 id 从 2 ➜ 4; - React 检测到 key 变了,直接删除 + 创建,不做复用。
你可以加个 console.log 或 useEffect 来观察每一项是否重新渲染。
💥 React diff 的底层实现:Fiber 架构
如果你对源码感兴趣,React 的 diff 不再是老版本“递归同步执行”,而是基于 Fiber 架构 的:
- 支持任务拆分(可以暂停/恢复);
- 实现异步可中断更新;
- 给每个组件分配 Fiber 节点,记录前后状态。
但对于大多数开发者,了解「diff 做的是“找不同 + 最小更新”」就已经能应付 80% 的场景和面试题了!
📌 面试官可能会这样问你:
🧑💻 面试官:React 为什么推荐你使用
key?key有什么作用?
答法推荐:
- React 使用
key来判断哪些 DOM 节点可以复用; - 减少重排重绘,提高性能;
- 使用 index 作为 key 可能导致状态错乱;
- React diff 的核心思想是最小化 DOM 操作。
✅ 总结一下本文核心知识:
| 关键点 | 说明 |
|---|---|
| diff 是干嘛的 | 比较新旧虚拟 DOM,最小化 DOM 更新 |
| 不做全量对比 | 同层级对比、类型不同直接替换 |
| key 的重要性 | 提高识别效率,避免状态错乱 |
| 不建议用 index 做 key | 会导致错误复用、UI 闪烁问题 |
| Fiber 是新架构 | 支持异步、时间切片、可中断渲染 |
🚀 结语:写 React,别只会 setState,得懂它背后的“找不同”机制!
React diff 是 React 性能优化的基础。理解了 diff,你才真正走进了 React 的内功世界。
如果你面试时说出上面那些点,面试官一定会在心里想: “这哥们,不止能写页面,还懂框架底层,稳!”
如果你觉得本文有帮助,不妨点赞收藏 + 关注,一起卷进更深的前端知识世界吧!