數據與視圖的綁定與同步,最終體現在對數據的讀寫處理過程中,也就是 Object.defineProperty() 定義的數據 set、get 函數中。Vue 中對於的函數為 defineReactive。
function defineReactive(obj, key, value) { var dep = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { if (Dep.target) { dep.depend() } return value }, set: function reactiveSetter(newVal) { if (value === newVal) { return } else { value = newVal dep.notify() } } }) }
在對數據進行讀取時,如果當前有 Watcher(對數據的觀察者吧,watcher 會負責將獲取的新數據發送給視圖),那將該 Watcher 綁定到當前的數據上(dep.depend(),dep 關聯當前數據和所有的 watcher 的依賴關系),是一個檢查並記錄依賴的過程。而在對數據進行賦值時,如果數據發生改變,則通知所有的 watcher(借助 dep.notify())。這樣,即便是我們手動改變了數據,框架也能夠自動將數據同步到視圖。
Vue 和 AngularJS 中,都是通過在 HTML 中添加指令的方式,將視圖元素與數據的綁定關系進行聲明。例如:
<form id="test"> <input type="text" v-model="name"> </form>
以上的 HTML 代碼表示該 input 元素與 name 數據進行綁定。在 JS 代碼中可以這樣進行初始化:
var vm = new Vue({ el: '#test', data: { name: 'luobo' } })
代碼正確執行后,頁面上 input 元素對應的位置會顯示上面代碼中給出的初始值:luobo。
由於雙向數據綁定已經建立,因此:
- 執行
vm.name = 'mickey'
后,頁面上 input 也會更新為顯示: mickey - 在頁面文本框中修改內容為:tang,則通過
vm.name
獲取的值為:"tang"
那么初始化的過程中,Vue 是如何識別出這種綁定關系的呢?
通過分析源碼,在初始化過程中(new Vue() 執行時),主要執行兩個步驟:
- compile
- link
compile 過程中,對於給定的目標元素進行解析,識別出所有綁定在元素(通過 el 屬性傳入)上的指令。
link 過程中,建立這些指令與對應數據(通過 data 屬性傳入初始值)的綁定關系,並以數據的初始值進行渲染。綁定關系建立后,就可以雙向同步數據了。