模板轉換成視圖的過程
在底層實現中Vue會將模板編譯成渲染函數,當然我們也可以不寫模板,直接寫渲染函數,以獲得更好的控制。
渲染函數:渲染函數是用來生成Virtual DOM的;
VNode虛擬節點:vnode可以理解成dom節點的描述對象,它描述了應該怎樣去創建真實的DOM節點;
patch(patching算法):虛擬DOM最核心的部分,它可以將vnode渲染成真實的DOM。這個過程是對比新舊虛擬節點之間有哪些不同,然后根據對比結果找出需要更新的的節點進行更新。其實際作用是在現有DOM上進行修改來實現更新視圖的目的;
Virtual DOM
Virtual DOM用JS對象來描述dom的節點(VNode),這個對象至少包含標簽名( tag)、屬性(attrs)和子元素對象( children)這三個屬性。它是對真實 DOM 的抽象,最終可以通過一系列操作把這個對象轉化為真實的dom。
具體步驟為
Virtual DOM 本質上就是在 JS 和 DOM 之間做了一個緩存
Virtual DOM的作用
虛擬DOM的最終目標是將虛擬節點渲染到視圖上,但是如果直接使用虛擬節點覆蓋舊節點的話,會有很多不必要的DOM操作。例如,一個ul標簽下很多個li標簽,其中只有一個li有變化,這種情況下如果使用新的ul去替代舊的ul,因為這些不必要的DOM操作而造成了性能上的浪費。
為了避免不必要的DOM操作,虛擬DOM在虛擬節點映射到視圖的過程中,將虛擬節點與上一次渲染視圖所使用的舊虛擬節點(oldVnode)做對比,找出真正需要更新的節點來進行DOM操作,從而避免操作其他無需改動的DOM。
其實虛擬DOM在Vue.js主要做了兩件事:
提供與真實DOM節點所對應的虛擬節點vnode;
將虛擬節點vnode和舊虛擬節點oldVnode進行對比,然后更新視圖;
Virtual DOM的diff算法
遞歸地進行同級vnode的diff,最終實現整個DOM樹的更新
步驟:
用 JavaScript 對象結構表示 DOM 樹的結構;然后用這個樹構建一個真正的 DOM 樹,插到文檔當中;
當狀態變更的時候,重新構造一棵新的對象樹。然后用新的樹和舊的樹進行比較,記錄兩棵樹差異;
把所記錄的差異應用到所構建的真正的DOM樹上,視圖就更新了;
Virtual DOM的優點
跨平臺的優勢:由于 Virtual DOM 是以 JavaScript 對象為基礎而不依賴真實平臺環境,所以使它具有了跨平臺的能力,比如說瀏覽器平臺、Weex、Node 等;
提高效率:操作 DOM 慢,js運行效率高,所以將DOM對比操作放在JS層可以提高效率;
提高渲染性能:通過patch 的核心----diff 算法,找出本次DOM需要更新的節點來更新,其他的不更新。比如修改某個model 100次,從1加到100,那么有了Virtual DOM的緩存之后,只會把最后一次修改patch到view上。
nextTick
this.$nextTick(()=>{//操作。。。
})
1 Vue生命周期的created()鉤子函數進行的DOM操作一定要放在Vue.nextTick()的回調函數中;
2?當項目中你想在改變DOM元素的數據后基于新的dom做點什么,對新DOM一系列的js操作都需要放進Vue.nextTick()的回調函數中;
3?在使用某個第三方插件時 ,希望在vue生成的某些dom動態發生變化時重新應用該插件,也會用到該方法;
改變數據有時不更新
1 vue實現數據雙向綁定有這么一個過程:當你把一個普通的 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象所有的屬性,并使用Object.defineProperty() 把這些屬性全部轉為getter/setter。每個組件實例都有相應的 watcher 實例對象,它會在組件渲染的過程中把屬性記錄為依賴,之后當依賴項的 setter 被調用時,會通知 watcher 重新計算,從而致使它關聯的組件得以更新。實現數據data變化更新視圖view。
var vm = newVue({
data:{
a:1; //vm.a 是響應的
}
})
vm.b= 2; //vm.b 是非響應的
2?沒有更新dom是因為改變數據之后Object.defineproperty()的set方法沒有被觸發,即沒有監測到數據更新。以下幾種情況會出現這個問題:
當你利用索引直接設置數組的一項時,例如:this.items[indexOfItem] = newValue;
當你修改數組的長度時,例如:this.items.length = newLength;
數組的push,splice等方法也不會更新dom;
對象里邊的修改:
data () {return{
student: {
name:''},
teach:["李磊"]
}
}//以下操作不會觸發視圖更新
this.student.name="XXX";
解決辦法:
1 使用set:
this.$set('對象名', key, value); //對象寫法
this.$set(this.teach,0, “韓梅梅”); //數組寫法
2 使用deep
watch:{
student:{
handler:(n,o)=>{//邏輯處理
},
deep:true}
}
3? 改變原對象或數組的地址
this.obj = Object.assign({},this.obj,{"sex","man"});