1. 程式人生 > 實用技巧 >vue雙向資料繫結

vue雙向資料繫結

vue.js是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的settergetter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥來渲染檢視。

具體步驟:

第一步:需要observer的資料物件進行遞迴遍歷,包括子屬性物件的屬性,都加上 setter和getter
這樣的話,給這個物件的某個值賦值,就會觸發setter,那麼就能監聽到了資料變化

第二步:compile解析模板指令,將模板中的變數替換成資料,然後初始化渲染頁面檢視,並將每個指令對應的節點繫結更新函式,新增監聽資料的訂閱者,一旦資料有變動,收到通知,更新檢視

第三步:Watcher

訂閱者是ObserverCompile之間通訊的橋樑,主要做的事情是:
1、在自身例項化時往屬性訂閱器(dep)裡面新增自己
2、自身必須有一個update()方法
3、待屬性變動dep.notice()通知時,能呼叫自身的update()方法,並觸發Compile中繫結的回撥,則功成身退。

第四步:MVVM作為資料繫結的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model資料變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通訊橋樑,達到資料變化 -> 檢視更新;檢視互動變化(input) -> 資料model變更的雙向繫結效果。

進一步理解

1. 什麼是setter、getter

答:首先,別誤以為他們就是一會要說的get、set

物件有兩種屬性:

  1. 資料屬性:就是我們經常使用的屬性
  2. 訪問器屬性:也稱存取器屬性(存取器屬性就是一組獲取和設定值的函式)

  

資料屬性就是a和b;

get和set就是關鍵字 它們後面各自對應一個函式,這個函式就是上面紅字部分所講的,儲存器屬性。

get對應的方法稱為getter,負責獲取值,它不帶任何引數。set對應的方法為setter,負責設定值,在它的函式體中,一切的return都是無效的。

2. 什麼是Object.defineProperty() ?

答:我們先看一句定義:

物件是由多個名/值對組成的無序的集合。物件中每個屬性對應任意型別的值。

定義物件可以使用建構函式或字面量的形式:

除了以上新增屬性的方式,當然還可以使用Object.defineProperty定義新屬性或修改原有的屬性;

語法:

Object.defineProperty(obj, prop, descriptor)

引數:

obj:必需。目標物件;
prop:必需。需定義或修改的屬性的名字;
descriptor:必需。目標屬性所擁有的特性;

返回值:

傳入函式的物件,即第一個引數obj;

OK,定義介紹完了,我們現在說一下一會關於雙向繫結我們要用到的知識點:存取器描述;(誒?你是不是發現和上面好像有點關係)

對頭,它是這樣使用的:

Vue是採用資料劫持結合釋出/訂閱模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。

在vue中v-modelv-name{{}}等都可以對資料進行展示,也就是說假如一個屬性都通過這三個指令了,那麼每當這個屬性改變的時候,相應的這個三個指令的html檢視也必須改變;

於是vue中就是每當有這樣的可能用到雙向繫結的指令,就在一個Dep中增加一個訂閱者(addSub),其訂閱者只是更新自己的指令對應的資料,也就是v-model='name'{{name}}有兩個對應的訂閱者,各自管理自己的地方;

每當屬性的set方法觸發,就迴圈更新Dep中的訂閱者(notify);

就是Observer一旦有了set觸發,就會通知到Dep,那Dep接到通知之後呢?從圖上來看,下面所講的就應該是Compile了,也很簡單:

compile主要做的事情是解析模板指令,將模板中的變數替換成資料 主要做的工作

1)初始化,init的時候 初始化渲染頁面檢視;

2)將每個指令對應的節點繫結更新函式,新增監聽資料的訂閱者;

Dep負責維護依賴,而訂閱者則來自於compile,一旦有資料變動,則會繫結更新函式,此時也就是產生了訂閱者,這個時候Dep內就增加了一個訂閱者,而一旦資料變動,則會收到通知,更新檢視;

好了,你是不是覺得上面這行說不通,或是讀不通,當然,因為上面的這個流程了缺少了,我們最後要說的Watcher,我把上面這句話補全,就是Watcher的工作了;

Dep負責維護依賴,而訂閱者則來自於compile,一旦有資料變動,則會通過Watcher繫結更新函式,此時Watcher也向Dep中添加了訂閱者,一旦Dep接到Observer的通知,它就會再去通知Watcher,Watcher則會呼叫自身的update()方法,並觸發Compile中繫結的回撥,更新檢視;

最後敲黑板:

首先我們為每個vue屬性用Object.defineProperty()實現資料劫持,為每個屬性分配一個訂閱者集合的管理陣列dep;

然後在編譯的時候在該屬性的陣列dep中新增訂閱者,v-model會新增一個訂閱者,{{}}也會,v-bind也會,只要用到該屬性的指令理論上都會;

接著為input會新增監聽事件,修改值就等於為該屬性賦值,則會觸發該屬性的set方法,在set方法內通知訂閱者陣列dep,訂閱者陣列迴圈呼叫各訂閱者的update方法更新檢視。

這樣我們就能實現js的雙向資料繫結,隨著文字框輸入文字的變化,span中會同步顯示相同的文字內容;這樣就實現了model => view以及view => model的雙向繫結。
通過給in輸入框新增事件監聽input來觸發obj物件的set方法,而set再修改了訪問器屬性的同時,也修改了dom樣式,改變了span標籤內的文字。