對(duì)于這個(gè)問題脚囊,我想說(shuō),如果你找到了比虛擬dom更加好的方案桐磁,你當(dāng)然可以直接使用這么一套方案去植入到框架中悔耘,來(lái)解決框架的問題。
我認(rèn)為引入虛擬dom我擂,是因?yàn)殚_發(fā)框架的人為了給我們開發(fā)者提供更高的開發(fā)效率衬以,不需要關(guān)心繁瑣的dom操作,減少心智負(fù)擔(dān)校摩,提高代碼的可維護(hù)性等開發(fā)出了這么一套框架出來(lái)之后看峻,如何操作dom的問題,就轉(zhuǎn)移到了框架那邊了衙吩。
框架當(dāng)然可以在每次數(shù)據(jù)改變之后互妓,把之前渲染的頁(yè)面全部不要,重新根據(jù)我們最新的數(shù)據(jù)生成一個(gè)新的頁(yè)面坤塞,但是冯勉,這樣做,效率是不是太低了摹芙?
仔細(xì)想想灼狰,我們可以發(fā)現(xiàn),我們是不是可以復(fù)用之前頁(yè)面的那些dom元素呢浮禾?
如果這一次渲染出來(lái)的頁(yè)面跟上一次渲染出來(lái)的頁(yè)面交胚,有很多地方都是可以重復(fù)應(yīng)用的坛悉,我們完全可以只改動(dòng)那些真正發(fā)生變化的地方。這樣一來(lái)承绸,問題又變成了我們要如何知道有哪些地方?jīng)]有變化裸影,哪些地方又真正發(fā)生了變化呢?
這時(shí)框架開發(fā)者們就想出了引入虛擬dom這么一種解決方案
我們先來(lái)了解一下虛擬dom是什么:
我們可以理解為军熏,虛擬dom的本質(zhì)其實(shí)就是一個(gè)普通的js對(duì)象而已轩猩,它里面有一些屬性,描述了一個(gè)真實(shí)dom的一些核心的屬性和父子結(jié)構(gòu)荡澎。如:
const div = {
tag: 'div',
props: {
'id': '#app'
},
children: [{
tag: 'p',
children: 'this is a p'
}]
}
這個(gè)div變量保存的對(duì)象就可以看作是一個(gè)虛擬dom均践。
在我們了解虛擬dom是什么之后,我們?cè)賮?lái)看一下框架開發(fā)者們是如何將它應(yīng)用到框架中去的摩幔。
框架開發(fā)者做出了一套響應(yīng)式系統(tǒng)后彤委,就有能力給我們開發(fā)者提供當(dāng)數(shù)據(jù)發(fā)生變化時(shí),通知某一些函數(shù)重新運(yùn)行或衡。在框架內(nèi)部焦影,維護(hù)著這么一個(gè)流程:
render函數(shù)的執(zhí)行,會(huì)返回一個(gè)虛擬dom封断,我們用vnode代替斯辰。當(dāng)數(shù)據(jù)發(fā)生變化,通知我們的render重新運(yùn)行坡疼,運(yùn)行之后彬呻,拿到最新的vnode,也就是描述這次數(shù)據(jù)變更之后柄瑰,我們最新的視圖長(zhǎng)什么樣子闸氮。然后并沒有直接根據(jù)這個(gè)最新的vnode來(lái)操作真實(shí)dom來(lái)渲染最新的視圖,而是將最新的vnode與之前舊的vnode教沾,進(jìn)行一個(gè)對(duì)比蒲跨,找到真正需要更新的地方,再去進(jìn)行真實(shí)的dom操作详囤,從而完成視圖的重新渲染财骨。
有了這個(gè)一個(gè)中間層镐作,相比于之前不管三七二十一直接根據(jù)最新的vnode來(lái)渲染視圖藏姐,性能上肯定是提高了不少。
那么現(xiàn)在该贾,為了進(jìn)一步提高性能羔杨,得想出一個(gè)辦法,怎么樣去最快得找出新舊vnode的不同之處呢杨蛋?也就是找出相對(duì)來(lái)說(shuō)性能較高的diff算法兜材。
在vue2中理澎,使用了雙端diff,也就是雙指針曙寡。具體我就不在這里展開了糠爬,大家有興趣可以去了解了解,我之后也會(huì)再寫一篇文章聊聊
至此举庶,對(duì)于為什么需要虛擬dom执隧,我就已經(jīng)講完了。對(duì)于網(wǎng)上有些爭(zhēng)論户侥,說(shuō)什么虛擬dom效率跟直接操作真實(shí)dom效率要高镀琉,我覺得是還沒有真正理解到位。越接近底層蕊唐,性能越好屋摔,框架是基于原生js實(shí)現(xiàn)的,框架也就是在更上層替梨,性能只能說(shuō)是無(wú)限接近原生js钓试,想超越是不可能。
舉個(gè)例子:有一個(gè)頁(yè)面副瀑,點(diǎn)擊某個(gè)按鈕亚侠,需要改變某個(gè)元素的文本,你覺得使用框架(vnode)來(lái)做俗扇,跟不使用框架(vnode)來(lái)做硝烂,哪個(gè)效率更高?
先來(lái)看看不使用框架铜幽,我們只需要在點(diǎn)擊的時(shí)候滞谢,直接操作真實(shí)dom的api即可完成效果。
我們?cè)賮?lái)看看使用框架除抛,對(duì)于框架來(lái)說(shuō)狮杨,我們不需要關(guān)心如何操作dom了,我們只要點(diǎn)擊的時(shí)候到忽,給與元素的文本綁定的數(shù)據(jù)重新賦值即可達(dá)到效果橄教,可是,框架的渲染是有一套固定的流程的喘漏,首先再對(duì)數(shù)據(jù)重新賦值的時(shí)候护蝶,框架內(nèi)部通知了render函數(shù)相關(guān)的watcher,然后調(diào)用了watcher函數(shù)的update方法翩迈,這里面有把render的運(yùn)行放入了一個(gè)微任務(wù)隊(duì)列持灰,同步代碼執(zhí)行完后,將這個(gè)微任務(wù)取出來(lái)執(zhí)行负饲,也就是執(zhí)行我們的render函數(shù)堤魁,這里面各種創(chuàng)建vnode喂链,最終拿到最新的vnode,這還沒完妥泉,還需要跟舊vnode進(jìn)行dom diff椭微,最終找到了就是那個(gè)元素的文本需要變化,這時(shí)調(diào)用真正的dom api完成文本的改變盲链。(以上只是描述了一個(gè)大概的渲染流程赏表,沒有具體展開了)
通過對(duì)比,不用我多說(shuō)了吧匈仗,細(xì)品...