面試必問(wèn):React和Vue各自是如何更新視圖的?
在 Vue 3 中,響應(yīng)式系統(tǒng)是基于 Proxy
代理實(shí)現(xiàn)的,而 React 的狀態(tài)管理和更新機(jī)制則完全不同。React 并沒有使用 Proxy
,它主要依賴 狀態(tài)不可變性(Immutability) 和 調(diào)度(Reconciliation) 機(jī)制來(lái)觸發(fā)組件更新。
?? React 是如何實(shí)現(xiàn)狀態(tài)更新并觸發(fā) UI 變化的?
1?? State 是不可變的
React 組件的 state
是不可變的,每次更新狀態(tài)時(shí),我們并不會(huì)直接修改原來(lái)的 state,而是創(chuàng)建一個(gè)新的 state 對(duì)象,并用它替換舊的 state。例如:
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // 生成新的 state,觸發(fā)更新
};
React 通過(guò) setState
或 useState
提供的 setCount
來(lái)觸發(fā)狀態(tài)更新。因?yàn)?nbsp;count + 1
返回的是一個(gè)新的值,所以 React 認(rèn)為狀態(tài)已變更。
2?? 觸發(fā) Re-render
當(dāng) state
發(fā)生變化時(shí),React 不會(huì)立即更新 UI,而是會(huì):
- 觸發(fā) 調(diào)度(Scheduling),將當(dāng)前組件標(biāo)記為需要更新;
- 合并多個(gè)狀態(tài)更新,在下一次渲染時(shí)一起應(yīng)用;
- 重新執(zhí)行組件函數(shù),得到新的
JSX
結(jié)構(gòu)。
示例:
function Counter() {
const [count, setCount] = useState(0);
console.log("組件渲染了"); // 每次 state 更新時(shí),組件都會(huì)重新執(zhí)行
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
每次 setCount
觸發(fā)時(shí),React 不會(huì)直接修改 DOM,而是會(huì)重新執(zhí)行 Counter
組件函數(shù),生成新的 JSX
,然后 React 通過(guò) Diffing 算法 找出變化的地方,再進(jìn)行高效的 DOM 更新。
3?? Diffing 和 Reconciliation 機(jī)制
當(dāng) state
發(fā)生變化時(shí),React 并不會(huì)暴力重新渲染整個(gè)頁(yè)面,而是:
- 比較(Diffing)新舊 Virtual DOM,找出變化的部分;
- 高效更新(Reconciliation) 只修改受影響的 DOM 片段。
示例:
function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello</h1>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
在 setCount(count + 1)
觸發(fā)后:
1.React 重新執(zhí)行 App
組件,生成新的 Virtual DOM
:
<div>
<h1>Hello</h1>
<p>Count: 1</p> // 變化的地方
<button>Increment</button>
</div>
2.React 發(fā)現(xiàn) h1
和 button
沒變,只更新 <p>
標(biāo)簽的文本內(nèi)容,而不是整個(gè) div
。
4?? React 為什么不用 Proxy?
Vue 3 通過(guò) Proxy
直接監(jiān)聽對(duì)象的變化,實(shí)現(xiàn)細(xì)粒度的響應(yīng)式,而 React 依賴 不可變數(shù)據(jù) 和 Virtual DOM: ? React 方案的優(yōu)勢(shì):
- 避免直接修改對(duì)象,防止副作用(確保狀態(tài)變更可預(yù)測(cè))。
- 優(yōu)化性能:通過(guò)
Diffing
機(jī)制減少不必要的 DOM 更新。 - 適用于函數(shù)式編程,配合
useState
,useReducer
等 hook 進(jìn)行狀態(tài)管理。
? 缺點(diǎn):
- 需要手動(dòng)使用
setState
,不像 Vue 那樣直接修改對(duì)象就會(huì)自動(dòng)觸發(fā)更新。 - 深層嵌套對(duì)象需要手動(dòng)更新:
const [user, setUser] = useState({ name: "Alice", age: 25 });
setUser({ ...user, age: 26 }); // 不能直接 user.age = 26
- Vue 通過(guò)
Proxy
代理可以自動(dòng)檢測(cè)到user.age = 26
的變化,而 React 需要?jiǎng)?chuàng)建新對(duì)象才能觸發(fā)更新。
?? 總結(jié):React 如何更新 UI?
- 狀態(tài)不可變(Immutability):每次修改
state
時(shí),必須創(chuàng)建新的對(duì)象或值。 - 組件重新執(zhí)行:當(dāng)
state
變化時(shí),React 重新運(yùn)行組件函數(shù),返回新的JSX
結(jié)構(gòu)。 - Diffing & Reconciliation:
- React 比較新舊
Virtual DOM
,找出變化的部分; - 只更新需要修改的 DOM 節(jié)點(diǎn),而不是整個(gè)頁(yè)面。
?? 這就是 React 不用 Proxy
也能高效更新 UI 的關(guān)鍵!
Vue 3 的更新過(guò)程主要依賴 響應(yīng)式系統(tǒng)(基于 Proxy
) 和 虛擬 DOM(Virtual DOM),它能夠高效地追蹤數(shù)據(jù)變化,并只更新需要修改的部分 DOM。下面詳細(xì)解析 Vue 3 的更新過(guò)程。
?? Vue 3 是如何觸發(fā) UI 更新的?
1?? 響應(yīng)式系統(tǒng)(Reactive System)
Vue 3 通過(guò) Proxy
代理對(duì)象的 get
和 set
操作,實(shí)現(xiàn)自動(dòng)追蹤依賴 & 觸發(fā)更新。當(dāng)你修改 state
時(shí),Vue 會(huì):
- 追蹤數(shù)據(jù)(依賴收集)
- 檢測(cè)變化(觸發(fā)更新)
- 重新渲染 Virtual DOM 并更新真實(shí) DOM
?? 示例:Vue 3 如何監(jiān)聽數(shù)據(jù)變化
import { reactive } from "vue";
const state = reactive({ count: 0 });
console.log(state.count); // 訪問(wèn)屬性時(shí),Vue 記錄依賴
state.count++; // 修改屬性時(shí),Vue 觸發(fā)更新
Vue 3 通過(guò) Proxy
代理 state
,監(jiān)聽 count
變化,自動(dòng)通知視圖更新。
Vue 2 使用
Object.defineProperty()
只能監(jiān)聽對(duì)象已有的屬性,Vue 3 的Proxy
解決了這個(gè)局限,支持監(jiān)聽新增/刪除的屬性。
2?? 依賴收集(Dependency Collection)
Vue 需要知道哪些組件或計(jì)算屬性依賴 state.count
,這樣當(dāng) count
變化時(shí),它只會(huì)更新受影響的組件,而不是整個(gè)應(yīng)用。
工作流程:
- 當(dāng)組件渲染時(shí),Vue 訪問(wèn)
state.count
,觸發(fā)get
,將該組件注冊(cè)為count
的依賴(收集副作用effect
)。 - 以后
count
變化時(shí),Vue 會(huì)通知所有依賴它的地方更新。
示例:
import { reactive, effect } from "vue";
const state = reactive({ count: 0 });
// 創(chuàng)建一個(gè)副作用(Effect)
effect(() => {
console.log("Count changed:", state.count);
});
state.count++; // 修改時(shí)觸發(fā) effect,打印 "Count changed: 1"
在 Vue 組件內(nèi)部,effect()
由 Vue 自動(dòng)管理,開發(fā)者無(wú)需手動(dòng)調(diào)用。
3?? 觸發(fā)更新(Trigger & Scheduler)
當(dāng) state.count++
發(fā)生時(shí),Vue 觸發(fā) set
操作:
- Vue 先檢查
count
是否真的變化(新值 !== 舊值)。 - 如果變化了,Vue 通知
effect
(視圖更新邏輯)重新執(zhí)行。 - Vue 使用 調(diào)度器(Scheduler) 合并多個(gè)狀態(tài)更新,避免不必要的重復(fù)渲染。
示例:多個(gè)狀態(tài)變化會(huì)合并更新
<script setup>
import { reactive } from "vue";
const state = reactive({ count: 0 });
function increment() {
state.count++;
state.count++; // Vue 不會(huì)觸發(fā)兩次 DOM 更新,而是合并優(yōu)化
}
</script>
<template>
<p>{{ state.count }}</p>
<button @click="increment">+2</button>
</template>
Vue 通過(guò) nextTick()
機(jī)制合并更新,減少 DOM 操作,提高性能。
4?? 重新渲染 Virtual DOM
當(dāng) state.count
變化后:
- Vue 重新執(zhí)行組件的渲染函數(shù),生成新的 Virtual DOM(虛擬 DOM) 結(jié)構(gòu)。
- Vue 對(duì)比新舊 Virtual DOM(Diffing 算法),找出變化的部分。
- Vue 只更新變更的 DOM 節(jié)點(diǎn),而不是整個(gè)頁(yè)面。
5?? Diffing & Patch 過(guò)程
Vue 3 使用 Patch Algorithm 進(jìn)行高效的 DOM 更新:
- 如果 Virtual DOM 結(jié)構(gòu)沒變(只是內(nèi)容變了),Vue 直接更新文本內(nèi)容。
- 如果子元素順序發(fā)生變化,Vue 采用最小修改策略,只移動(dòng)必要的節(jié)點(diǎn),而不是全部重繪。
示例:
<template>
<ul>
<li v-for="item in list" :key="item.id">{{ item.text }}</li>
</ul>
</template>
<script setup>
import { reactive } from "vue";
const list = reactive([
{ id: 1, text: "Vue" },
{ id: 2, text: "React" }
]);
setTimeout(() => {
list.reverse(); // Vue 只會(huì)調(diào)整 DOM 位置,而不會(huì)重新創(chuàng)建 <li>
}, 2000);
</script>
Vue 只移動(dòng) <li>
位置,而不會(huì)銷毀 & 重新創(chuàng)建整個(gè)列表。
?? Vue 3 的 UI 更新完整流程
1?? 訪問(wèn)響應(yīng)式數(shù)據(jù)(Proxy get
) → 觸發(fā)依賴收集
2?? 數(shù)據(jù)變更(Proxy set
) → 觸發(fā)更新(Effect 重新執(zhí)行)
3?? Vue 重新執(zhí)行渲染函數(shù),生成新的 Virtual DOM
4?? Vue 進(jìn)行 Diffing,找出變更的 DOM 節(jié)點(diǎn)
5?? Vue 使用 Patch 機(jī)制,僅更新需要修改的部分 DOM
相比 Vue 2,Vue 3 在依賴追蹤、調(diào)度和 Virtual DOM 更新上更高效!
?? Vue 3 和 React 更新機(jī)制對(duì)比
特性 | Vue 3 | React |
響應(yīng)式原理 |
代理數(shù)據(jù),自動(dòng)追蹤依賴 |
/ |
依賴收集 | 訪問(wèn)數(shù)據(jù)時(shí)自動(dòng)收集( | 組件渲染時(shí)自動(dòng)關(guān)聯(lián) state |
狀態(tài)變更 |
操作觸發(fā)更新 |
觸發(fā)更新 |
組件更新機(jī)制 | 重新運(yùn)行 渲染函數(shù) 生成 VDOM | 重新執(zhí)行 組件函數(shù) 生成 VDOM |
Diff 算法 | 只更新變化的部分 | 只更新變化的部分 |
批量更新 |
自動(dòng)合并 | React 事件中 |
?? Vue 3 的 Proxy 監(jiān)聽數(shù)據(jù)變更,React 通過(guò) useState
返回新對(duì)象。Vue 自動(dòng)追蹤依賴,而 React 需要手動(dòng) setState
。
?? 總結(jié):Vue 3 是如何更新界面的?
- Proxy 監(jiān)聽數(shù)據(jù)變化,訪問(wèn)數(shù)據(jù)時(shí)自動(dòng)收集依賴,修改數(shù)據(jù)時(shí)自動(dòng)觸發(fā)更新。
- 依賴收集(Effect 機(jī)制),組件只會(huì)在需要更新時(shí)重新渲染,而不會(huì)整個(gè)應(yīng)用重繪。
- 批量更新(Scheduler),合并多個(gè)
setState
操作,減少不必要的渲染。 - Diffing & Patch 機(jī)制,Vue 只更新最小范圍的 DOM,而不是整個(gè)頁(yè)面。
?? Vue 3 更新機(jī)制的核心優(yōu)勢(shì)
? 更精準(zhǔn)的依賴追蹤:基于 Proxy
,比 Vue 2 更高效,支持監(jiān)聽新增/刪除屬性? 更少的渲染開銷:只有真正變化的組件才會(huì)重新渲染? 更高效的 DOM 更新:使用 Diff 算法 & Patch 機(jī)制,只更新必要部分
Vue 3 結(jié)合了響應(yīng)式系統(tǒng) + Virtual DOM,讓 UI 更新更智能、更高效。希望這個(gè)解析對(duì)你理解 Vue 3 的更新機(jī)制有幫助!