vuejs是虛擬DOM的;Vue.js2.0就引入了Virtual DOM(虛擬DOM)機制,這讓初始渲染速度提升了2-4倍,並大大降低了記憶體消耗。虛擬DOM的優勢:可以跨平台、提高效率、提升渲染效能等。
本教學操作環境:windows7系統、vue2.9.6版,DELL G3電腦。
Vue.js 2.0引入Virtual DOM,比Vue.js 1.0的初始渲染速度提升了2-4倍,並大幅降低了記憶體消耗。那麼,什麼是Virtual DOM?為什麼需要Virtual DOM?它是透過什麼方式去提升頁面渲染效率的呢?這是本文要探討的問題。
在正式介紹Virtual Dom之前,我們有必要先了解下模板轉換成視圖的過程整個過程(如下圖):
簡單點講,在Vue的底層實作上,Vue將模板編譯成虛擬DOM渲染函數。結合Vue自帶的回應系統,在狀態改變時,Vue能夠智慧地計算出重新渲染元件的最小代價並應到DOM操作。
我們先對上圖幾個概念加以解釋:
#渲染函數:渲染函數是用來產生Virtual DOM的。 Vue建議使用模板來建立我們的應用程式介面,在底層實作中Vue會將模板編譯成渲染函數,當然我們也可以不寫模板,直接寫渲染函數,以獲得更好的控制。
VNode 虛擬節點:它可以代表一個真實的 dom 節點。透過 createElement 方法能將 VNode 渲染成 dom 節點。簡單地說,vnode可以理解成節點描述物件,它描述了應該怎麼做去創建真實的DOM節點。
patch(也叫做patching演算法):虛擬DOM最核心的部分,它可以將vnode渲染成真實的DOM,這個過程是比較新舊虛擬節點之間有哪些不同,然後根據對比結果找出需要更新的節點進行更新。這點我們從單字意義就可以看出, patch本身就有補丁、修補的意思,其實際作用是在現有DOM上進行修改來實現更新視圖的目的。 Vue的Virtual DOM Patching演算法是基於Snabbdom的實現,並在些基礎上作了很多的調整和改進。
Virtual DOM 其實就是一棵以 JavaScript 物件( VNode 節點)作為基礎的樹,用物件屬性來描述節點,其實它只是一層對真實 DOM 的抽象。最終可以透過一系列操作使這棵樹映射到真實環境。
簡單來說,可以把Virtual DOM 理解為一個簡單的JS對象,並且最少包含標籤名( tag)、屬性(attrs)和子元素對象( children)三個屬性。不同的框架對這三個屬性的命名會有點差別。
對於虛擬DOM,咱們來看一個簡單的實例,就是下圖所示的這個,詳細的闡述了模板→ 渲染函數→ 虛擬DOM樹→ 真實DOM
的一個過程
虛擬DOM的最終目標是將虛擬節點渲染到視圖上。但是如果直接使用虛擬節點覆蓋舊節點的話,會有很多不必要的DOM操作。例如,一個ul標籤下很多個li標籤,其中只有一個li有變化,這種情況下如果使用新的ul去替代舊的ul,因為這些不必要的DOM操作而造成了性能上的浪費。
為了避免不必要的DOM操作,虛擬DOM在虛擬節點映射到視圖的過程中,將虛擬節點與上一次渲染視圖所使用的舊虛擬節點(oldVnode)做對比,找出真正需要更新的節點來進行DOM操作,從而避免操作其他無需改變的DOM。
其實虛擬DOM在Vue.js主要做了兩件事:
因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)
Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
为了实现高效的DOM操作,一套高效的虚拟DOM diff算法显得很有必要。我们通过patch 的核心----diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新。比如修改某个model 100次,从1加到100,那么有了Virtual DOM的缓存之后,只会把最后一次修改patch到view上。那diff 算法的实现过程是怎样的?
Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n3)变成O(n)。
diff 算法包括几个步骤:
diff 算法的实现过程
diff 算法本身非常复杂,实现难度很大。本文去繁就简,粗略介绍以下两个核心函数实现流程:
1. patch(container,vnode)
通过这个函数可以让VNode渲染成真正的DOM,我们通过以下模拟代码,可以了解大致过程:
function createElement(vnode) { var tag = vnode.tag var attrs = vnode.attrs || {} var children = vnode.children || [] if (!tag) { return null } // 创建真实的 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 添加子元素,如果还有子节点,则递归的生成子节点。 elem.appendChild(createElement(childVnode)) // 递归 }) // 返回真实的 DOM 元素 return elem }
2. patch(vnode,newVnode)
这里我们只考虑vnode与newVnode如何对比的情况:
function updateChildren(vnode, newVnode) { var children = vnode.children || [] var newChildren = newVnode.children || [] // 遍历现有的children children.forEach(function (childVnode, index) { var newChildVnode = newChildren[index] // 两者tag一样 if (childVnode.tag === newChildVnode.tag) { // 深层次对比,递归 updateChildren(childVnode, newChildVnode) } else { // 两者tag不一样 replaceNode(childVnode, newChildVnode) } } )}
相关推荐:《vue.js教程》
以上是vuejs是虛擬DOM嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!