您好,登錄后才能下訂單哦!
這篇文章主要講解了“vue.js是不是虛擬DOM”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“vue.js是不是虛擬DOM”吧!
本教程操作環(huán)境:windows7系統(tǒng)、vue2.9.6版,DELL G3電腦。
Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,并大大降低了內(nèi)存消耗。那么,什么是Virtual DOM?為什么需要Virtual DOM?它是通過(guò)什么方式去提升頁(yè)面渲染效率的呢?這是本文所要探討的問(wèn)題。
在正式介紹 Virtual Dom之前,我們有必要先了解下模板轉(zhuǎn)換成視圖的過(guò)程整個(gè)過(guò)程(如下圖):
Vue.js通過(guò)編譯將template 模板轉(zhuǎn)換成渲染函數(shù)(render ) ,執(zhí)行渲染函數(shù)就可以得到一個(gè)虛擬節(jié)點(diǎn)樹(shù)
在對(duì) Model 進(jìn)行操作的時(shí)候,會(huì)觸發(fā)對(duì)應(yīng) Dep 中的 Watcher 對(duì)象。Watcher 對(duì)象會(huì)調(diào)用對(duì)應(yīng)的 update 來(lái)修改視圖。這個(gè)過(guò)程主要是將新舊虛擬節(jié)點(diǎn)進(jìn)行差異對(duì)比,然后根據(jù)對(duì)比結(jié)果進(jìn)行DOM操作來(lái)更新視圖。
簡(jiǎn)單點(diǎn)講,在Vue的底層實(shí)現(xiàn)上,Vue將模板編譯成虛擬DOM渲染函數(shù)。結(jié)合Vue自帶的響應(yīng)系統(tǒng),在狀態(tài)改變時(shí),Vue能夠智能地計(jì)算出重新渲染組件的最小代價(jià)并應(yīng)到DOM操作上。
我們先對(duì)上圖幾個(gè)概念加以解釋:
渲染函數(shù):渲染函數(shù)是用來(lái)生成Virtual DOM的。Vue推薦使用模板來(lái)構(gòu)建我們的應(yīng)用界面,在底層實(shí)現(xiàn)中Vue會(huì)將模板編譯成渲染函數(shù),當(dāng)然我們也可以不寫(xiě)模板,直接寫(xiě)渲染函數(shù),以獲得更好的控制。
VNode 虛擬節(jié)點(diǎn):它可以代表一個(gè)真實(shí)的 dom 節(jié)點(diǎn)。通過(guò) createElement 方法能將 VNode 渲染成 dom 節(jié)點(diǎn)。簡(jiǎn)單地說(shuō),vnode可以理解成節(jié)點(diǎn)描述對(duì)象,它描述了應(yīng)該怎樣去創(chuàng)建真實(shí)的DOM節(jié)點(diǎn)。
patch(也叫做patching算法):虛擬DOM最核心的部分,它可以將vnode渲染成真實(shí)的DOM,這個(gè)過(guò)程是對(duì)比新舊虛擬節(jié)點(diǎn)之間有哪些不同,然后根據(jù)對(duì)比結(jié)果找出需要更新的的節(jié)點(diǎn)進(jìn)行更新。這點(diǎn)我們從單詞含義就可以看出, patch本身就有補(bǔ)丁、修補(bǔ)的意思,其實(shí)際作用是在現(xiàn)有DOM上進(jìn)行修改來(lái)實(shí)現(xiàn)更新視圖的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的實(shí)現(xiàn),并在些基礎(chǔ)上作了很多的調(diào)整和改進(jìn)。
Virtual DOM 其實(shí)就是一棵以 JavaScript 對(duì)象( VNode 節(jié)點(diǎn))作為基礎(chǔ)的樹(shù),用對(duì)象屬性來(lái)描述節(jié)點(diǎn),實(shí)際上它只是一層對(duì)真實(shí) DOM 的抽象。最終可以通過(guò)一系列操作使這棵樹(shù)映射到真實(shí)環(huán)境上。
簡(jiǎn)單來(lái)說(shuō),可以把Virtual DOM 理解為一個(gè)簡(jiǎn)單的JS對(duì)象,并且最少包含標(biāo)簽名( tag)、屬性(attrs)和子元素對(duì)象( children)三個(gè)屬性。不同的框架對(duì)這三個(gè)屬性的命名會(huì)有點(diǎn)差別。
對(duì)于虛擬DOM,咱們來(lái)看一個(gè)簡(jiǎn)單的實(shí)例,就是下圖所示的這個(gè),詳細(xì)的闡述了模板 → 渲染函數(shù) → 虛擬DOM樹(shù) → 真實(shí)DOM
的一個(gè)過(guò)程
虛擬DOM的最終目標(biāo)是將虛擬節(jié)點(diǎn)渲染到視圖上。但是如果直接使用虛擬節(jié)點(diǎn)覆蓋舊節(jié)點(diǎn)的話,會(huì)有很多不必要的DOM操作。例如,一個(gè)ul標(biāo)簽下很多個(gè)li標(biāo)簽,其中只有一個(gè)li有變化,這種情況下如果使用新的ul去替代舊的ul,因?yàn)檫@些不必要的DOM操作而造成了性能上的浪費(fèi)。
為了避免不必要的DOM操作,虛擬DOM在虛擬節(jié)點(diǎn)映射到視圖的過(guò)程中,將虛擬節(jié)點(diǎn)與上一次渲染視圖所使用的舊虛擬節(jié)點(diǎn)(oldVnode)做對(duì)比,找出真正需要更新的節(jié)點(diǎn)來(lái)進(jìn)行DOM操作,從而避免操作其他無(wú)需改動(dòng)的DOM。
其實(shí)虛擬DOM在Vue.js主要做了兩件事:
提供與真實(shí)DOM節(jié)點(diǎn)所對(duì)應(yīng)的虛擬節(jié)點(diǎn)vnode
將虛擬節(jié)點(diǎn)vnode和舊虛擬節(jié)點(diǎn)oldVnode進(jìn)行對(duì)比,然后更新視圖
具備跨平臺(tái)的優(yōu)勢(shì)
由于 Virtual DOM 是以 JavaScript 對(duì)象為基礎(chǔ)而不依賴真實(shí)平臺(tái)環(huán)境,所以使它具有了跨平臺(tái)的能力,比如說(shuō)瀏覽器平臺(tái)、Weex、Node 等。
操作 DOM 慢,js運(yùn)行效率高。我們可以將DOM對(duì)比操作放在JS層,提高效率。
因?yàn)镈OM操作的執(zhí)行速度遠(yuǎn)不如Javascript的運(yùn)算速度快,因此,把大量的DOM操作搬運(yùn)到Javascript中,運(yùn)用patching算法來(lái)計(jì)算出真正需要更新的節(jié)點(diǎn),最大限度地減少DOM操作,從而顯著提高性能。
Virtual DOM 本質(zhì)上就是在 JS 和 DOM 之間做了一個(gè)緩存??梢灶惐?CPU 和硬盤(pán),既然硬盤(pán)這么慢,我們就在它們之間加個(gè)緩存:既然 DOM 這么慢,我們就在它們 JS 和 DOM 之間加個(gè)緩存。CPU(JS)只操作內(nèi)存(Virtual DOM),最后的時(shí)候再把變更寫(xiě)入硬盤(pán)(DOM)
提升渲染性能
Virtual DOM的優(yōu)勢(shì)不在于單次的操作,而是在大量、頻繁的數(shù)據(jù)更新下,能夠?qū)σ晥D進(jìn)行合理、高效的更新。
為了實(shí)現(xiàn)高效的DOM操作,一套高效的虛擬DOM diff算法顯得很有必要。我們通過(guò)patch 的核心----diff 算法,找出本次DOM需要更新的節(jié)點(diǎn)來(lái)更新,其他的不更新。比如修改某個(gè)model 100次,從1加到100,那么有了Virtual DOM的緩存之后,只會(huì)把最后一次修改patch到view上。那diff 算法的實(shí)現(xiàn)過(guò)程是怎樣的?
Vue的diff算法是基于snabbdom改造過(guò)來(lái)的,僅在同級(jí)的vnode間做diff,遞歸地進(jìn)行同級(jí)vnode的diff,最終實(shí)現(xiàn)整個(gè)DOM樹(shù)的更新。因?yàn)榭鐚蛹?jí)的操作是非常少的,忽略不計(jì),這樣時(shí)間復(fù)雜度就從O(n3)變成O(n)。
diff 算法包括幾個(gè)步驟:
用 JavaScript 對(duì)象結(jié)構(gòu)表示 DOM 樹(shù)的結(jié)構(gòu);然后用這個(gè)樹(shù)構(gòu)建一個(gè)真正的 DOM 樹(shù),插到文檔當(dāng)中
當(dāng)狀態(tài)變更的時(shí)候,重新構(gòu)造一棵新的對(duì)象樹(shù)。然后用新的樹(shù)和舊的樹(shù)進(jìn)行比較,記錄兩棵樹(shù)差異
把所記錄的差異應(yīng)用到所構(gòu)建的真正的DOM樹(shù)上,視圖就更新了
diff 算法的實(shí)現(xiàn)過(guò)程
diff 算法本身非常復(fù)雜,實(shí)現(xiàn)難度很大。本文去繁就簡(jiǎn),粗略介紹以下兩個(gè)核心函數(shù)實(shí)現(xiàn)流程:
patch(container,vnode) :初次渲染的時(shí)候,將VDOM渲染成真正的DOM然后插入到容器里面。
patch(vnode,newVnode):再次渲染的時(shí)候,將新的vnode和舊的vnode相對(duì)比,然后之間差異應(yīng)用到所構(gòu)建的真正的DOM樹(shù)上。
1. patch(container,vnode)
通過(guò)這個(gè)函數(shù)可以讓VNode渲染成真正的DOM,我們通過(guò)以下模擬代碼,可以了解大致過(guò)程:
function createElement(vnode) { var tag = vnode.tag var attrs = vnode.attrs || {} var children = vnode.children || [] if (!tag) { return null } // 創(chuàng)建真實(shí)的 DOM 元素 var elem = document.createElement(tag) // 屬性 var attrName for (attrName in attrs) { if (attrs.hasOwnProperty(attrName)) { // 給 elem 添加屬性 elem.setAttribute(attrName, attrs[attrName]) } } // 子元素 children.forEach(function (childVnode) { // 給 elem 添加子元素,如果還有子節(jié)點(diǎn),則遞歸的生成子節(jié)點(diǎn)。 elem.appendChild(createElement(childVnode)) // 遞歸 }) // 返回真實(shí)的 DOM 元素 return elem }
2. patch(vnode,newVnode)
這里我們只考慮vnode與newVnode如何對(duì)比的情況:
function updateChildren(vnode, newVnode) { var children = vnode.children || [] var newChildren = newVnode.children || [] // 遍歷現(xiàn)有的children children.forEach(function (childVnode, index) { var newChildVnode = newChildren[index] // 兩者tag一樣 if (childVnode.tag === newChildVnode.tag) { // 深層次對(duì)比,遞歸 updateChildren(childVnode, newChildVnode) } else { // 兩者tag不一樣 replaceNode(childVnode, newChildVnode) } } )}
感謝各位的閱讀,以上就是“vue.js是不是虛擬DOM”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)vue.js是不是虛擬DOM這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。