React、Vue框架如何實(shí)現(xiàn)組件更新,原理是什么?
原文合集地址如下,有需要的朋友可以關(guān)注
本文地址
合集地址
引言
React 和 Vue 都是當(dāng)今最流行的前端框架,它們都實(shí)現(xiàn)了組件化開發(fā)模式。為了優(yōu)化性能,兩者都采用了虛擬DOM技術(shù)。當(dāng)組件狀態(tài)發(fā)生改變時(shí),它們會(huì)使用虛擬DOM進(jìn)行局部渲染比對(duì),只更新必要的DOM節(jié)點(diǎn),從而避免重新渲染整個(gè)組件樹。本文將從React和Vue的組件更新原理入手,剖析兩者虛擬DOM difer算法的異同點(diǎn)。React通過comparing virtual DOM components and re-rendering only difference,而Vue通過響應(yīng)式依賴追蹤確定組件invalidated狀態(tài)。盡管兩者技術(shù)實(shí)現(xiàn)不同,但目的都是實(shí)現(xiàn)增量更新提高性能。本文還將通過代碼實(shí)例,說明兩者的Domin difer流程、對(duì)比粒度、更新觸發(fā)等關(guān)鍵區(qū)別。讀者將對(duì)React和Vue增量更新的內(nèi)在原理有更深的理解,學(xué)會(huì)在實(shí)踐中根據(jù)應(yīng)用場(chǎng)景選擇更合適的框架。
React、Vue如何實(shí)現(xiàn)組件更新
React和Vue是兩個(gè)流行的JavaScript庫,用于構(gòu)建用戶界面。它們都有自己的組件,下面將簡單介紹一下更新原理。
React的組件更新機(jī)制: 在React中,組件更新是由虛擬DOM(Virtual DOM)和diff算法驅(qū)動(dòng)的。當(dāng)組件的狀態(tài)(state)或?qū)傩裕╬rops)發(fā)生變化時(shí),React會(huì)進(jìn)行虛擬DOM的重新渲染,并將新的虛擬DOM與舊的虛擬DOM進(jìn)行比較,找出需要更新的部分,然后只更新這些部分到實(shí)際的DOM。
React的組件更新流程如下:
組件狀態(tài)或?qū)傩园l(fā)生變化。
React調(diào)用組件的
render()
方法重新渲染虛擬DOM。React將新的虛擬DOM與舊的虛擬DOM進(jìn)行比較,找出需要更新的部分。
React通過最小化DOM操作,只更新需要變化的部分到實(shí)際的DOM。
組件更新完成,觸發(fā)相應(yīng)的生命周期方法(如
componentDidUpdate
)。
下面是一個(gè)簡單的React組件的例子,展示了組件的更新機(jī)制:
import?React,?{?Component?}?from?'react';
class?Counter?extends?Component?{
??constructor(props)?{
????super(props);
????this.state?=?{
??????count:?0
????};
??}
??handleClick?=?()?=>?{
????this.setState(prevState?=>?({
??????count:?prevState.count?+?1
????}));
??}
??render()?{
????return?(
??????<div>
????????<p>Count:?{this.state.count}</p>
????????<button?onClick={this.handleClick}>Increment</button>
??????</div>
????);
??}
}
在上面的例子中,當(dāng)用戶點(diǎn)擊"Increment"按鈕時(shí),handleClick
方法會(huì)更新組件的狀態(tài)count
。React會(huì)重新調(diào)用render()
方法重新渲染虛擬DOM,并將新的虛擬DOM與舊的虛擬DOM進(jìn)行比較,然后只更新變化的部分(這里是<p>Count: {this.state.count}</p>
)到實(shí)際的DOM。
Vue的組件更新機(jī)制: 在Vue中,組件更新是由響應(yīng)式系統(tǒng)驅(qū)動(dòng)的。Vue使用了一種名為"依賴追蹤"的機(jī)制,它會(huì)在組件渲染過程中追蹤組件所依賴的數(shù)據(jù),并建立起依賴關(guān)系。當(dāng)依賴的數(shù)據(jù)發(fā)生變化時(shí),Vue會(huì)通知相關(guān)的組件進(jìn)行更新。 Vue的更新過程大致如下:
數(shù)據(jù)變化時(shí),setter 觸發(fā)依賴,標(biāo)記組件為臟數(shù)據(jù)
在下一輪事件循環(huán)中,Vue 會(huì)調(diào)用 patch 函數(shù),比對(duì)新舊虛擬 DOM 樹
通過 diff 算法比較樹的差異,得到需要更新的最小節(jié)點(diǎn)
只對(duì)變化的部分進(jìn)行 DOM 操作,更新視圖 diff 算法的主要步驟是:
對(duì)比新舊節(jié)點(diǎn),是否為同一節(jié)點(diǎn)
如果不是,直接替換該節(jié)點(diǎn)及子節(jié)點(diǎn)
如果是,對(duì)比新舊節(jié)點(diǎn)的屬性是否變化
對(duì)比子節(jié)點(diǎn),使用鍵值優(yōu)化順序復(fù)雜度
遞歸對(duì)比所有子節(jié)點(diǎn) 通過這種方式,Vue 可以只更新變化的部分,避免不必要的 DOM 操作。 下面是一個(gè)簡單的Vue組件的例子,展示了組件的更新機(jī)制:
<template>
??<div>
????<p>Count:?{{?count?}}</p>
????<button?@click="increment">Increment</button>
??</div>
</template>
<script>
export?default?{
??data()?{
????return?{
??????count:?0
????};
??},
??methods:?{
????increment()?{
??????this.count++;
????}
??}
}
</script>
在上面的例子中,當(dāng)用戶點(diǎn)擊"Increment"按鈕時(shí),increment
方法會(huì)更新組件的數(shù)據(jù)count
。Vue會(huì)檢測(cè)到count
的變化,并通知組件重新渲染。然后Vue使用虛擬DOM進(jìn)行比較,只更新變化的部分(這里是<p>Count: {{ count }}</p>
)到實(shí)際的DOM。
總結(jié): React和Vue都采用了類似的組件更新機(jī)制,它們都通過比較虛擬DOM或追蹤依賴來實(shí)現(xiàn)高效的組件更新。React和Vue都使用虛擬DOM和diff算法,這些機(jī)制使得組件的更新變得高效,只更新必要的部分,提高了應(yīng)用的性能。
React與Vue更新的區(qū)別
Vue
使用數(shù)據(jù)響應(yīng)系統(tǒng),通過改變組件的數(shù)據(jù)屬性來觸發(fā)更新。
當(dāng)組件的 data、props、computed 等屬性改變時(shí),會(huì)觸發(fā) setter,標(biāo)記組件為“臟”。
在下一輪事件循環(huán)中,會(huì)批量觸發(fā)這些“臟”組件的重新渲染。
React
使用狀態(tài)(state)和屬性(props)來控制組件。
當(dāng)狀態(tài)或?qū)傩愿淖儠r(shí),會(huì)觸發(fā)重新渲染。
React 使用 Virtual DOM 來提高性能,只會(huì)針對(duì)改變的組件進(jìn)行最小化渲染。相同點(diǎn)
兩者都是聲明式框架,通過狀態(tài)/數(shù)據(jù)變化控制界面。
都使用虛擬 DOM ,進(jìn)行增量更新提高性能。區(qū)別
Vue 側(cè)重響應(yīng)式數(shù)據(jù),React 更側(cè)重狀態(tài)管理。
Vue 使用模板,React 使用 JSX。
Vue 批量異步更新,React 同步更新。
Vue 依賴數(shù)據(jù)變化觸發(fā)更新,React 通過 setState/useState 控制。 總體來說,兩者都使用了類似的虛擬DOM和增量更新機(jī)制,但在觸發(fā)更新的方式上有差異。Vue 更加主動(dòng),而 React 更加顯式地控制。
什么是Diff算法
diff 算法是虛擬 DOM 中用于增量更新的關(guān)鍵算法。它的主要作用是對(duì)比兩棵虛擬 DOM 樹的差異,運(yùn)算出需要更新的最小量 DOM 操作。 diff 算法的基本步驟如下:
用虛擬 DOM 構(gòu)建出新的DOM樹(樹A)
將新的DOM樹與舊的DOM樹(樹B)進(jìn)行對(duì)比找出差異
對(duì)比過程中,首先比較樹A和樹B的根節(jié)點(diǎn)
如果根節(jié)點(diǎn)不相同,直接替換整個(gè)DOM樹
如果根節(jié)點(diǎn)相同,再遞歸地對(duì)比和更新它的屬性、子節(jié)點(diǎn)等
只更新變化的部分,不修改相同的節(jié)點(diǎn)
最后將變化渲染到真實(shí)DOM中 diff算法的時(shí)間復(fù)雜度為O(n),它通過以下優(yōu)化進(jìn)一步提升了性能:
Web UI中DOM節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少,可以忽略不計(jì)
擁有相同類的兩個(gè)組件生成相似的樹形結(jié)構(gòu),擁有較高的移位率
通過唯一id區(qū)分節(jié)點(diǎn),可以根據(jù)id直接判斷兩個(gè)節(jié)點(diǎn)是否相同
這里是一個(gè)簡化的React Diff算法的實(shí)現(xiàn)示例。它包含了比較根節(jié)點(diǎn)、屬性和子節(jié)點(diǎn)的邏輯。當(dāng)根節(jié)點(diǎn)類型不同時(shí),創(chuàng)建新節(jié)點(diǎn)并替換舊節(jié)點(diǎn)。當(dāng)屬性不同時(shí),更新屬性。對(duì)于子節(jié)點(diǎn),通過遍歷舊子節(jié)點(diǎn)和新子節(jié)點(diǎn)來進(jìn)行比較,并進(jìn)行遞歸的Diff算法調(diào)用。根據(jù)比較結(jié)果,進(jìn)行增加、刪除或更新相應(yīng)的節(jié)點(diǎn)。
//?舊的虛擬DOM樹
let?oldVDOM?=?{
??tag:?'div',
??attrs:?{
????id:?'container'?
??},
??children:?[
????{tag:?'p',?attrs:?{class:?'paragraph'}},
????{tag:?'span',?attrs:?{class:?'span'}}
??]
}
//?新的虛擬DOM樹?
let?newVDOM?=?{
??tag:?'div',
??attrs:?{
????id:?'container'
??},
??children:?[
????{tag:?'p',?attrs:?{class:?'paragraph'}},
????{tag:?'span',?attrs:?{class:?'span-new'}}?//?span類名變化
??]
}
//?diff算法
function?diff(oldTree,?newTree)?{
??//?1.?比較根節(jié)點(diǎn)
??if(oldTree.tag?!==?newTree.tag)?{
????//?根節(jié)點(diǎn)不同,返回新樹
????return?newTree
??}
??
??//?2.?比較屬性
??if(oldTree.attrs.id?!==?newTree.attrs.id)?{
????//?id變化,更新屬性
????newTree.attrs?=?newTree.attrs?
??}
??
??//?3.?比較子節(jié)點(diǎn)
??constchildChanges?=?[]
??
??//?使用key進(jìn)行優(yōu)化
??oldTree.children.forEach(child?=>?{
????const?newChild?=?newTree.children.find(c?=>?c.key?===?child.key)
????
????//?深度遞歸對(duì)比子節(jié)點(diǎn)
????const?changedChild?=?diff(child,?newChild)
????childChanges.push(changedChild)
??})
??
??newTree.children?=?childChanges
??
??return?newTree
}
//?最終只會(huì)更新?span 的類名變化?
const?newVDOM?=?diff(oldVDOM,?newVDOM)?
兩者的diff算法的區(qū)別
Vue 和 React 雖然都采用了虛擬 DOM 和 diff 算法,但在具體的 diff 實(shí)現(xiàn)上還是有一些區(qū)別的:
對(duì)比粒度不同
Vue 的虛擬 DOM 是Render 函數(shù)渲染生成的,對(duì)比粒度為組件級(jí)別。
React 的虛擬 DOM 是由 React元素構(gòu)成,對(duì)比粒度為節(jié)點(diǎn)級(jí)別。
處理方式不同
Vue 通過標(biāo)記靜態(tài)子樹,可以重復(fù)使用不變的部分。
React 總是重新構(gòu)造虛擬 DOM,對(duì)相同節(jié)點(diǎn)也會(huì)進(jìn)行屬性對(duì)比。
組件識(shí)別不同
Vue 通過組件的 name 屬性識(shí)別組件是否相同。
React 通過組件 type 來判斷是否為相同組件類型。
key 的作用不同
Vue 主要用 key 管理可復(fù)用的元素。
React 主要用 key 匹配舊元素與新元素。
事件處理不同
Vue 可以精確知道哪個(gè)事件發(fā)生變化,只更新事件。
React 每次都需要重新綁定事件,對(duì)組件影響較大。 綜上,Vue 和 React 雖然概念上都是通過虛擬 DOM + diff 實(shí)現(xiàn)增量更新,但在具體實(shí)現(xiàn)和優(yōu)化上還是有一定區(qū)別的。