Vue.js雙向繫結的實現原理

來源:互聯網
上載者:User

標籤:完整   自己   發布   監聽   屬性   變化   image   傳回值   訂閱發布   

       Vue.js最核心的功能有兩個,一是響應式的資料繫結系統,二是組件系統。本文僅探究幾乎所有Vue的開篇介紹都會提到的hello world雙向繫結是怎樣實現的。先講涉及的知識點,再參考源碼,用儘可能少的代碼實現那個hello world開篇樣本。

   參考文章:1190000006599500

一、訪問器屬性

       訪問器屬性是對象中的一種特殊屬性,它不能直接在對象中設定,而必須通過defineProperty()方法單獨定義。

       var obj = { };

       // 為obj定義一個名為hello的訪問器屬性

       Object.defineProperty(obj, "hello", {

         get: function () {return sth},

         set: function (val) {/* do sth */}

       })

       obj.hello // 可以像普通屬性一樣讀取存取器屬性

       訪問器屬性的"值"比較特殊,讀取或設定訪問器屬性的值,實際上是調用其內部特性:get和set函數。

       obj.hello // 讀取屬性,就是調用get函數並返回get函數的傳回值

       obj.hello = "abc" // 為屬性賦值,就是調用set函數,賦值其實是傳參

 

       get和set方法內部的this都指向obj,這意味著get和set函數可以操作對象內部的值。另外,訪問器屬性的會"覆蓋"同名的普通屬性,因為訪問器屬性會被優先訪問,與其同名的普通屬性則會被忽略(也就是所謂的被"劫持"了)。

 

二、極簡雙向繫結的實現

 

       此例實現的效果是:隨文字框輸入文字的變化,span中會同步顯示相同的文字內容;在js或控制台顯式的修改obj.name的值,視圖會相應更新。這樣就實現了model =>view以及view => model的雙向繫結,並且是響應式的。

 

       以上就是Vue實現雙向繫結的基本原理。

 

三、分解任務

       上述樣本僅僅是為了說明原理。我們最終要實現的是:

 

 

       首先將該任務分成幾個子任務:

   1、輸入框以及文本節點與data中的資料繫結

   2、輸入框內容變化時,data中的資料同步變化。即view => model的變化。

   3、data中的資料變化時,文本節點的內容同步變化。即model => view的變化。

       要實現任務一,需要對DOM進行編譯,這裡有一個知識點:DocumentFragment。

 

四、DocumentFragment

       DocumentFragment(文檔片段)可以看作節點容器,它可以包含多個子節點,當我們將它插入到DOM中時,只有它的子節點會插入目標節點,所以把它看作一組節點的容器。使用DocumentFragment處理節點,速度和效能遠遠優於直接操作DOM。Vue進行編譯時間,就是將掛載目標的所有子節點劫持(真的是劫持)到DocumentFragment中,經過一番處理後,再將DocumentFragment整體返回插入掛載目標。

      

五、資料初始化綁定

       以上代碼實現了任務一,我們可以看到,hello world已經呈現在輸入框和文本節點中。

 

六、響應式的資料繫結

       再來看任務二的實現思路:當我們在輸入框輸入資料的時候,首先觸發input事件(或者keyup、change事件),在相應的事件處理常式中,我們擷取輸入框的value並賦值給vm執行個體的text屬性。我們會利用defineProperty將data中的text劫持為vm的訪問器屬性,因此給vm.text賦值,就會觸發set方法。在set方法中主要做兩件事,第一是更新屬性的值,第二留到任務三再說。

       任務二也就完成了,text屬性值會與輸入框的內容同步變化:

 

七、訂閱/發布模式(subscribe&publish)

       text屬性變化了,set方法觸發了,但是文本節點的內容沒有變化。如何讓同樣綁定到text的文本節點也同步變化呢?這裡又有一個知識點:訂閱發布模式。

       訂閱發布模式(又稱觀察者模式)定義了一種一對多的關係,讓多個觀察者同時監聽某一個主題對象,這個主題對象的狀態發生改變時就會通知所有觀察者對象。

       發行者發出通知 => 主題對象收到通知並推送給訂閱者 => 訂閱者執行相應操作

       之前提到的,當set方法觸發後做的第二件事就是作為發行者發出通知:“我是屬性text,我變了”。文本節點則是作為訂閱者,在收到訊息後執行相應的更新操作。

 

八、雙向繫結的實現

       回顧一下,每當new一個Vue,主要做了兩件事:第一個是監聽資料:observe(data),第二個是編譯HTML:nodeToFragement(id)。

       在監聽資料的過程中,會為data中的每一個屬性產生一個主題對象dep。

       在編譯HTML的過程中,會為每個與資料繫結相關的節點產生一個訂閱者watcher,watcher會將自己添加到相應屬性的dep中。

       我們已經實現:修改輸入框內容 => 在事件回呼函數中修改屬性值 => 觸發屬性的set方法。

       接下來我們要實現的是:發出通知dep.notify() => 觸發訂閱者的update方法 => 更新視圖。

       這裡的關鍵邏輯是:如何將watcher添加到關聯屬性的dep中。

       在編譯HTML過程中,為每個與data關聯的節點產生一個Watcher。Watcher函數中發生了什麼呢?

       首先,將自己賦給了一個全域變數Dep.target;

       其次,執行了update方法,進而執行了get方法,get的方法讀取了vm的訪問器屬性,從而觸發了訪問器屬性的get方法,get方法中將該watcher添加到了對應訪問器屬性的dep中;

       再次,擷取屬性的值,然後更新視圖。

       最後,將Dep.target設為空白。因為它是全域變數,也是watcher與dep關聯的唯一橋樑,任何時刻都必須保證Dep.target只有一個值。

       至此,hello world雙向繫結就基本實現了。常值內容會隨輸入框內容同步變化,在控制器中修改vm.text的值,會同步反映到常值內容中。

   完整代碼:https://github.com/bison1994/two-way-data-binding

Vue.js雙向繫結的實現原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.