Ⅰ vue數據雙向綁定原理
vue.js 採用數據劫持結合發布者-訂閱者模式的方式,通過 Object.defineProperty() 來劫持各個屬性的setter,getter,在數據變動時發布消息給訂閱者,觸發相應的監聽回調。
首先我們為每個vue屬性用Object.defineProperty()實現數據劫持,在監聽數據的過程中,為每個屬性分配一個訂閱者集合的管理數組dep;然後在編譯的時候在該屬性的數組dep中添加訂閱者 watcher,v-model會添加一個訂閱者,{{}}也會,v-bind也會,只要用到該屬性的指令理論上都會,接著為input會添加監聽事件,修改值就會為該屬性賦值,觸發該屬性的set方法,在set方法內通知訂閱者數組dep,訂閱者數組循環調用各訂閱者的update方法更新視圖。
實現步驟:修改輸入框內容 => 在事件回調函數中修改屬性值 => 觸發屬性的 set 方法=>發出通知 dep.notify() => 觸發訂閱者的 update 方法 => 更新視圖。
流程圖 :
在實例化一個Vue對象的時候,會傳進去一個data對象,之後分成兩個進程,一個進程是對掛載目標元素模板里的v-model和{{ }};兩個指令進行編譯。另一個進程是對傳進去的data對象裡面的數據進行監聽。
上圖中,observe是利用Object.defineProperty()對傳入的data對象進行數據監聽,在數據改變的時候觸發該屬性的set方法,更新該屬性的值,並發布消息,我(該屬性)的值變了。
compile是編譯器,找到vue的指令v-model所在的元素,將data中該屬性的值賦給元素的value,並給這個元素添加二級監聽器,在元素的值改變的時候,將新值賦給data裡面同名屬性,這個時候就完成了單向數據綁定,視圖 >> 模型。
那麼最終的由模型到視圖的更新,依賴於dep和watcher,dep會收集訂閱者,就是綁定了data裡面屬性的元素,在數據更新的時候,會觸發該屬性的set方法,在set里觸發該屬性的消息發布通知函數。而Watcher根據收到的數據變化通知,更新相應的數據。
dep這個東東給大家解釋一下,就是data里的每個屬性都有一個dep對象,dep對象里可以有很多訂閱者(watcher),但是只有一個添加訂閱者的方法和一個發布變化通知的方法,就是模板上可以有多處元素綁定data里的同一個屬性值,所以dep是依賴於data裡面的屬性的。
而Watcher是每個{{ }}有一個,初次編譯的時候,會在new的時候自動更新一下模板的數據,等到下次數據改變的時候,由dep通知數據更新,直接調用watcher的update方法,更新模板的綁定數據。
observer 模塊共分為這幾個部分:
示意圖如下:
Observer的構造函數
value是需要被觀察的數據對象,在構造函數中,會給value增加 ob 屬性,作為數據已經被Observer觀察的標志。如果value是數組,就使用observeArray遍歷value,對value中每一個元素調用observe分別進行觀察。如果value是對象,則使用walk遍歷value上每個key,對每個key調用defineReactive來獲得該key的set/get控制權。
Dep是Observer與Watcher之間的紐帶,也可以認為Dep是服務於Observer的訂閱系統。Watcher訂閱某個Observer的Dep,當Observer觀察的數據發生變化時,通過Dep通知各個已經訂閱的Watcher。
Watcher是用來訂閱數據的變化的並執行相應操作(例如更新視圖)的。Watcher的構造器函數定義如下:
參數中,vm表示組件實例,expOrFn表示要訂閱的數據欄位(字元串表示,例如a.b.c)或是一個要執行的函數,cb表示watcher運行後的回調函數,options是選項對象,包含deep、user、lazy等配置。
Object.defineProperty(obj, prop, descriptor) ,這個語法內有三個參數,分別為 obj (要定義屬性的對象) prop (要定義或修改的屬性的名稱或 Symbol ) descriptor (要定義或修改的屬性描述符=>具體的改變方法)
簡單地說,就是用這個方法來定義一個值。當調用時我們使用了它裡面的get方法,當我們給這個屬性賦值時,又用到了它裡面的set方法;
主要解釋第三個參數 {
value: 設置屬性的值
writable: 值是否可以重寫。true | false
enumerable: 目標屬性是否可以被枚舉。true | false (就是能不能被遍歷出來)
configurable: 目標屬性是否可以被刪除或是否可以再次修改特性 true | false
set: 目標屬性設置值的方法
get:目標屬性獲取值的方法
}
set 是一個函數,接收一個新值,會在值被重寫或修改的時候觸發這個函數
get 是一個函數,返回一個值,會在屬性被調用的時候觸發。
注 :
Object.defineProperty()詳解
Object.defineProperty()官方文檔
已經了解到vue是通過數據劫持的方式來做數據綁定的,其中最核心的方法便是通過Object.defineProperty()來實現對屬性的劫持,那麼在設置或者獲取的時候我們就可以在get或者set方法里假如其他的觸發函數,達到監聽數據變動的目的。
我們知道通過Object.defineProperty()可以實現數據劫持,它的屬性在賦值的時候觸發set方法,
當然要是這么粗暴,肯定不行,性能會出很多的問題。
observer用來實現對每個vue中的data中定義的屬性循環用Object.defineProperty()實現數據劫持,以便利用其中的setter和getter,然後通知訂閱者,訂閱者會觸發它的update方法,對視圖進行更新。
為什麼要訂閱者 :在vue中v-model,v-name,{{}}等都可以對數據進行顯示,也就是說假如一個屬性都通過這三個指令了,那麼每當這個屬性改變的時候,相應的這個三個指令的html視圖也必須改變,於是vue中就是每當有這樣的可能用到雙向綁定的指令,就在一個Dep中增加一個訂閱者,其訂閱者只是更新自己的指令對應的數據,也就是v-model='name'和{{name}}有兩個對應的訂閱者,各自管理自己的地方。每當屬性的set方法觸發,就循環更新Dep中的訂閱者。
訂閱發布模式(又稱觀察者模式)定義了一種一對多的關系,讓多個觀察者同時監聽某一個主題對象,這個主題對象的狀態發生改變時就會通知所有觀察者對象。
發布者發出通知 => 主題對象收到通知並推送給訂閱者 => 訂閱者執行相應操作
舉個例子:
2.實現compile: compile的目的就是解析各種指令稱真正的html。
這樣一來就實現了vue的數據雙向綁定。
參考鏈接:
理解VUE雙向數據綁定原理和實現---趙佳樂
Vue的雙向數據綁定原理
vue雙向綁定原理分析
Vue原理解析之observer模塊
深入響應式原理