一、什麼是VNode
(1)Vue.js中存在一個VNode類,使用它可以實例化不同類型的vnode實例,而不同類型的vnode實例各自表示不同類型的DOM元素。
(2)其實vnode只是一個名字,本質上其實是Javascript中一個普通的對象,是從VNode類實例化的對象。我們用這個Javascript對象來描述一個真實DOM元素的話,那麼該DOM元素上的所有屬性在VNode這個對象上都存在對應的屬性。
(3)vnode可以理解成節點描述對象,它描述了應該怎樣去創建真實的DOM節點。例如tag表示一個元素節點的名稱,text表示一個文本節點的文本,children表示子節點等。
(4)vnode表示一個真實的DOM元素,所有真實的DOM節點都使用vnode創建並插入到頁面中。(vnode=》DOM=》視圖)
(5)vnode和視圖是一一對應的。我們可以把vnode理解成Javascript對象版本的DOM元素。
(6)渲染視圖的過程是先創建vnode,然後再使用vnode去生成真實的DOM元素,最後插入頁面渲染視圖。
二、VNode的作用
(1)由於每次渲染視圖時都是先創建vnode,然後使用它創建真實DOM插入到頁面中,所以可以將上一次渲染視圖時所創建的vnode緩存起來,之後每當需要重新渲染視圖時,將新創建的vnode和上一次緩存的vnode進行對比,查看它們之間有哪些不一樣的地方,找出這些不一樣的地方並基於此去修改真實的DOM。
(2)Vue.js目前對狀態的偵測策略採用了中等粒度。當狀態發生改變時,只通知到組件級別,然後組件內使用虛擬DOM來渲染視圖。
(3)當某個狀態發生改變時,只通知使用了這個狀態的組件。
(4)只要組件使用的衆多狀態中有一個發生了變化,那麼整個組件就要重新渲染。
三、Vnode的類型
(1)vnode的類型
註釋節點
文本節點
元素節點
組件節點
函數式組件
克隆節點
(2)vnode是Javascript中的一個對象,不同類型的vnode之間其實只是屬性不同,準確地說是有效屬性不同。因爲當使用VNode類創建一個vnode時,通過參數爲實例設置屬性時,無效的屬性會默認被賦值爲undefined或false。對於vnode身上無效屬性,直接忽略就好。
-
註釋節點
export const createEmptyVNode = text =>{
const node = new VNode();
node.text = text;
node.isComment = true;
return node
}
(1)一個註釋節點只有兩個有效屬性-----text和isComment,其餘屬性全是默認的undefined或則false。
// 註釋節點
//對應的vnode如下:
{
text:"註釋節點",
isComment:true
}
-
文本節點
export function createTextVNode(val){
return new VNode(undefined,undefined,undefined,String(val))
}
(1)文本類型的vnode被創建時,它只有一個text屬性
//文本類型的vnode
{
text:"Hello Berwin"
}
-
克隆節點
(1)克隆節點是將現有節點的屬性賦值到新節點中,讓新創建的節點和被克隆的節點的屬性保持一致,從而實現克隆效果。
(2)作用:優化靜態節點和插槽節點(slot node)。
(3)以靜態節點爲例,當組件內的某個狀態發生變化後,當前組件會通過虛擬DOM重新渲染視圖,靜態節點因爲它的內容不會改變,所以除了首次渲染需要執行渲染函數獲取vnode之外,後續更新不需要執行渲染函數重新生成vnode。因此,這時就會使用創建克隆節點的方法將vnode克隆一份,使用克隆節點進行渲染。這樣就不需要重新執行渲染函數生成新的靜態節點的vnode,從而提升一定程度的性能。
export function cloneVNode(vnode,deep){
const cloned = new VNode(
vnode.tag,
vnode.data,
vnode.children,
vnode.text,
vnode.elm,
vnode.context,
vnode.componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.;
cloned.isStatic = vnode.isStatic;
cloned.key = vnode.key;
cloned.isComment = vnode.isComment;
cloned.isCloned = true;
if(deep&&vnode.children){
cloned.chlidren = cloneVNodes(vnode.children);
}
return cloned;
}
(4)克隆現有節點時,只需要將現有節點的屬性全部賦值到新節點中即可。
(5)克隆節點和被克隆節點之間的唯一區別是isCloned屬性,克隆節點的isCloned爲true,被克隆的原始節點的isCloned爲false。
-
元素節點
(1)元素節點通常存在以下4種有效屬性。
- tag:tag是一個節點的名稱,例如ul、p、li和div等。
- data:該屬性包含了一些節點上的數據,比如attrs、class和style等。
- children:當前節點的子節點列表。
- context:它是當前組件的Vue.js實例。
(2)例子
真實的元素節點
<p><span>Hello</span><span>Berwin</span></p>
對應的vnode
{
children:[VNode,VNode],
context:{...},
data:{...},
tag:"p",
....
}
-
組件節點
(1)組件節點和元素節點類似,有以下兩個獨有的屬性
- componentOptions:組件節點的選項參數,其中包含propsData、tag和children等信息。
- componentInstance:組件的實例,也就是Vue.js實例。事實上,在Vue.js種,每個組件都是一個Vue.js實例。
(2)例子
一個組件節點
<child></child>
對應的vnode如下:
{
componentInstance:{...},
componentOptions:{...},
context:{...},
data:{...},
tag:"vue-component-1-child",
....
}
-
函數式組件
(1)函數式組件和組件節點類似,它有兩個獨有的屬性functionalContext和functionalOptions。
(2)通常,一個函數式組件的vnode是下面的樣子
{
functionalContext:{...},
functionalOptions:{...},
context:{...},
data:{...},
tag:"div",
....
}
四、總結
(1)vnode是一個類,可以生成不同類型的vnode實例,而不同類型的vnode表示不同類型的真實DOM元素。
(2)由於Vue.js對組件採用了虛擬DOM來更新視圖,當屬性發生變化時,整個組件都要進行重新渲染的操作,但組件內並不是所有DOM節點都需要更新,所以將vnode緩存並將當前新生成的vnode和上一次緩存的oldVnode進行對比,只對需要更新的部分進行DOM操作可以提升很多性能。
(3)vnode有多種類型,它們本質上都是從Vnode類實例化出的對象,其唯一區別只是屬性不同。