Vue 的深入原理是從 Vue 的響應(yīng)式實(shí)現(xiàn)深入到其精度如下所示:
var data = { a: 1 } observe(data) data.a = 2 // 觸發(fā) setter console.log(data.a) // ->2
Vue 會(huì)將 data 對(duì)象轉(zhuǎn)換為響應(yīng)式對(duì)象,然后它會(huì)重寫(xiě) data.a 的 setter 函數(shù),并且留下一個(gè)副本。當(dāng)我們?yōu)?data.a 賦值時(shí),setter 中的代碼會(huì)先更新 Vue 內(nèi)部的副本,然后再通知相關(guān)組件進(jìn)行更新。
如果 Vue 響應(yīng)式系統(tǒng)的實(shí)現(xiàn)過(guò)于管道流,則在組件層次很深的情況下會(huì)出現(xiàn)顯著的性能問(wèn)題。因此,Vue 使用了類似于虛擬DOM的技術(shù),用一棵類似DOM樹(shù)的東西來(lái)代表狀態(tài)。在組件內(nèi)部,我們就可以保持中心狀態(tài),而不必?fù)?dān)心父組件會(huì)插手。這種方法在性能上更加高效,因?yàn)樗鼫p少了更新操作的數(shù)量。
此時(shí),我們了解響應(yīng)式對(duì)象的實(shí)現(xiàn)原理并知道 Vue 為什么能自動(dòng)檢測(cè)到數(shù)據(jù)的變化,那么我們接下來(lái)看一下這個(gè)深層監(jiān)控方式的問(wèn)題:
var obj = { a: { b: 1 } } observe(obj) obj.a.b = 2 // 觸發(fā) setter console.log(obj.a.b) // ->2
前面我們說(shuō)過(guò),Vue 會(huì)將數(shù)據(jù)轉(zhuǎn)換為響應(yīng)式對(duì)象并代替 setter 函數(shù),因此,只有當(dāng)我們給 obj.a.b 賦新值時(shí),代碼才會(huì)運(yùn)行,并且才會(huì)使組件重新渲染。但是,在上面的代碼中,我們只需要給 obj.a 或 b 賦新值,即會(huì)觸發(fā) setter 函數(shù)和重新渲染組件。這會(huì)導(dǎo)致浪費(fèi)大量的計(jì)算資源,顯著影響性能。
為了避免這種類型的問(wèn)題,Vue會(huì)使用遞歸響應(yīng)式攔截,也就是只在最開(kāi)頭即綁定在組件實(shí)例屬性上的對(duì)象上進(jìn)行響應(yīng)式攔截,不會(huì)攔截整個(gè)屬性鏈路。因此,在上面的例子中,只有當(dāng)我們給 obj.a 或 a.b 賦新值時(shí),代碼才會(huì)運(yùn)行,如果相應(yīng)屬性是新添加的,則需要使用Vue.set api添加響應(yīng)式數(shù)據(jù), Vue 會(huì)使用 API 中提供的方法來(lái)添加響應(yīng)式數(shù)據(jù)。
總體而言,Vue 的響應(yīng)式實(shí)現(xiàn)中使用了虛擬 DOM 和遞歸攔截才能更好地實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的性能和效果。這也說(shuō)明了我們?cè)陂_(kāi)發(fā)大型應(yīng)用時(shí),必須對(duì) Vue 有更深入的了解,并且了解視圖狀態(tài)成為響應(yīng)式的這一過(guò)程也會(huì)影響你的代碼。