對于這個問題,我想說,如果你找到了比虛擬dom更加好的方案,你當然可以直接使用這么一套方案去植入到框架中,來解決框架的問題。
我認為引入虛擬dom,是因為開發(fā)框架的人為了給我們開發(fā)者提供更高的開發(fā)效率,不需要關心繁瑣的dom操作,減少心智負擔,提高代碼的可維護性等開發(fā)出了這么一套框架出來之后,如何操作dom的問題,就轉移到了框架那邊了。
框架當然可以在每次數(shù)據(jù)改變之后,把之前渲染的頁面全部不要,重新根據(jù)我們最新的數(shù)據(jù)生成一個新的頁面,但是,這樣做,效率是不是太低了?
仔細想想,我們可以發(fā)現(xiàn),我們是不是可以復用之前頁面的那些dom元素呢?
如果這一次渲染出來的頁面跟上一次渲染出來的頁面,有很多地方都是可以重復應用的,我們完全可以只改動那些真正發(fā)生變化的地方。這樣一來,問題又變成了我們要如何知道有哪些地方?jīng)]有變化,哪些地方又真正發(fā)生了變化呢?
這時框架開發(fā)者們就想出了引入虛擬dom這么一種解決方案
我們先來了解一下虛擬dom是什么:
我們可以理解為,虛擬dom的本質其實就是一個普通的js對象而已,它里面有一些屬性,描述了一個真實dom的一些核心的屬性和父子結構。如:
const div = {
tag: 'div',
props: {
'id': '#app'
},
children: [{
tag: 'p',
children: 'this is a p'
}]
}
這個div變量保存的對象就可以看作是一個虛擬dom。
在我們了解虛擬dom是什么之后,我們再來看一下框架開發(fā)者們是如何將它應用到框架中去的。
框架開發(fā)者做出了一套響應式系統(tǒng)后,就有能力給我們開發(fā)者提供當數(shù)據(jù)發(fā)生變化時,通知某一些函數(shù)重新運行。在框架內部,維護著這么一個流程:
render函數(shù)的執(zhí)行,會返回一個虛擬dom,我們用vnode代替。當數(shù)據(jù)發(fā)生變化,通知我們的render重新運行,運行之后,拿到最新的vnode,也就是描述這次數(shù)據(jù)變更之后,我們最新的視圖長什么樣子。然后并沒有直接根據(jù)這個最新的vnode來操作真實dom來渲染最新的視圖,而是將最新的vnode與之前舊的vnode,進行一個對比,找到真正需要更新的地方,再去進行真實的dom操作,從而完成視圖的重新渲染。
有了這個一個中間層,相比于之前不管三七二十一直接根據(jù)最新的vnode來渲染視圖,性能上肯定是提高了不少。
那么現(xiàn)在,為了進一步提高性能,得想出一個辦法,怎么樣去最快得找出新舊vnode的不同之處呢?也就是找出相對來說性能較高的diff算法。
在vue2中,使用了雙端diff,也就是雙指針。具體我就不在這里展開了,大家有興趣可以去了解了解,我之后也會再寫一篇文章聊聊
至此,對于為什么需要虛擬dom,我就已經(jīng)講完了。對于網(wǎng)上有些爭論,說什么虛擬dom效率跟直接操作真實dom效率要高,我覺得是還沒有真正理解到位。越接近底層,性能越好,框架是基于原生js實現(xiàn)的,框架也就是在更上層,性能只能說是無限接近原生js,想超越是不可能。
舉個例子:有一個頁面,點擊某個按鈕,需要改變某個元素的文本,你覺得使用框架(vnode)來做,跟不使用框架(vnode)來做,哪個效率更高?
先來看看不使用框架,我們只需要在點擊的時候,直接操作真實dom的api即可完成效果。
我們再來看看使用框架,對于框架來說,我們不需要關心如何操作dom了,我們只要點擊的時候,給與元素的文本綁定的數(shù)據(jù)重新賦值即可達到效果,可是,框架的渲染是有一套固定的流程的,首先再對數(shù)據(jù)重新賦值的時候,框架內部通知了render函數(shù)相關的watcher,然后調用了watcher函數(shù)的update方法,這里面有把render的運行放入了一個微任務隊列,同步代碼執(zhí)行完后,將這個微任務取出來執(zhí)行,也就是執(zhí)行我們的render函數(shù),這里面各種創(chuàng)建vnode,最終拿到最新的vnode,這還沒完,還需要跟舊vnode進行dom diff,最終找到了就是那個元素的文本需要變化,這時調用真正的dom api完成文本的改變。(以上只是描述了一個大概的渲染流程,沒有具體展開了)
通過對比,不用我多說了吧,細品...