一、引入
談及Vue中的數據雙向綁定,我們自然而然的想到是通過v-model指令實現的。但具體是怎么實現的呢?下面就介紹一下。
二、v-model語法糖是什么?原理是什么?
v-model語法糖是vue多個基礎語法(屬性綁定 和事件綁定)的簡寫。
原理:給表單元素input綁定通過"v-bind:"綁定value屬性的數據msg,再給表單元素input添加監聽value值改變的事件@input,給msg重新賦值。
代碼:
<!-- <input type="text" v-model="msg"> --> <input :value="msg" type="text" @input="msg=$event.target.value">
三、雙向數據綁定的原理?
vue中數據雙向綁定通過v-model實現,雙向綁定指的是視圖和內存中的數據進行雙向綁定。(通過Object.defineProperty和給元素注冊事件)
表單元素改變數據的原理:
- 實現數據驅動視圖
- 需要先定義一個data對象將其中msg屬性的值顯示到視圖中。想要通過Object.defineProperty來監測其值得變化是不可能實現的,因為使用他的同時需要給對象賦值並監聽,而data對象中已經有了這個屬性並綁定到了頁面中顯示,無法再重新定義該屬性。這就需要新建一個空對象vm,通過給vm定義屬性並監測來實現這個功能。
- Object.defineProperty調用時的第三個參數為對象配置項,其中的get()方法為vm的msg屬性值被獲取時觸發的回調函數。set()方法為vm的msg屬性值被設置時觸發的回調函數,set()函數的參數為msg屬性的新值。
- 實現視圖驅動數據
通過給表單元素注冊input事件,將表單元素中的value值賦給vue.msg
<!-- 1. 把數據渲染到表單元素中 --> <!-- 2. 當你修改數據的時候,表單元素的值發生改變,視圖需要更新 --> <!-- 3. 當你使用表單元素的時候,你在修改元素的值,對應的數據也要發生改變 --> <h5></h5> <input type="text"> <script> // const vm = new Vue({data:{msg:'hi vue'}}) vm.msg = '數據' // 使用用定義可以監聽(觀察)的屬性的。 const vm = {} const data = { msg: 'hi vue' } // 獲取dom const h5 = document.querySelector('h5') const input = document.querySelector('input') // 1. 默認渲染 h5.innerHTML = data.msg input.value = data.msg // 2.1 監聽 data中的msg的值改變 觀察 data中的msg的值改變 最終監聽的是vm的msg屬性變化 // 2.2 如果 數據改變 修改視圖 // 三個參數 給誰定義一個屬性 屬性的名稱 對象配置項(get 獲取屬性值 set 設置屬性值) Object.defineProperty(vm, 'msg', { get() { // 當獲取msg屬性值時觸發 // console.log('get') return data['msg'] }, set(newValue) { // 當設置msg屬性值時觸發 // console.log('set') // console.log(newValue) // 數據改變 data['msg'] = newValue // 修改視圖 h5.innerHTML = newValue input.value = newValue } }) // 3. 監聽 表單元素 的值改變事件 修改數據即可(上面已經實現數據驅動視圖) input.oninput = function(){ // console.log(this.value) vm.msg = this.value } </script>
總體描述:
-
Vue內部會把data中的數據通過defineProperty方法轉化為set和get的監控方式
-
當data中的數據發生變化時,會觸發對應的set或者get
-
修改屬性值的時候,觸發set方法
-
訪問屬性值的時候,觸發get方法
-
-
監控數據變化的目的還是為了更新頁面(僅僅更新數據變化對應的DOM節點:盡可能少的更新DOM)
-
但是完成上述要求需要底層虛擬DOM的支持
四、什么是虛擬DOM?
瀏覽器更新DOM比較耗時,為了節省時間,需要盡可能少的更新DOM。虛擬DOM是對真實DOM的一種描述;其組成部分虛擬節點也描述了真實的DOM節點,本質上就是普通對象
// VNODE 虛擬節點:描述了真實的DOM節點,本質上就是普通對象 { tagName: 'div' attrs: { class: "active", id: "info"} content: "hello" }
-
虛擬DOM也會形成一個樹狀結構,描述了真實的DOM樹(形成虛擬DOM樹和真實DOM樹的對應關系)
-
如果數據發生變化,那么就會觸發虛擬DOM數的對比(diff算法,過程發生在內存中)
-
對比的結果是:有變化的虛擬節點的集合
-
上述虛擬節點需要轉化為真實的DOM節點(下面是虛擬下面是虛擬節點轉換為真實節點的過程)
var div = document.createElement(vnode.tagName) div.setAttibute(key, value) div.innerHTML = vnode.content
- 最終會把真實的節點更新到頁面
雙向數據綁定運用虛擬DOM的進一步解釋說明:
在使用defineProperty進行到set()時,會采用虛擬DOM來跟新視圖;虛擬DOM有新、舊兩份數據存儲在內存中,每次監測到數據改變時,計算機會通過diff算法對比兩份數據的區別,得到有變化的虛擬節點的集合,並將它們轉換為真實的DOM節點更新到頁面中。
采用上面方式的原因就是瀏覽器解析完整DOM樹的時間、性能消耗,要遠比內存對比數據再進行局部渲染的消耗大得多。內存處理數據的速度要比瀏覽器快的多。