前端MVVM原理和雙向綁定簡析

MVC,MVP,MVVM三種軟件架構設計模式的對比:

MVC:

  1. M(model):數據模型,渲染模板
  2. V(view):視圖模板(用戶界面UI)
  3. C(controller):控制器,修改數據

MVP:

  1. M(model):數據模型
  2. V(view): 視圖模板
  3. P(presenter):控制展示器,修改數據,渲染模板

在MVP中View並不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的。

MVVM:

  1. M(model):數據模型
  2. V(view):視圖模板
  3. VM(viewModel):視圖模型,修改數據,自動渲染模板

MVVM模式和MVP一致,MVVM可以實現數據雙向綁定,viewModel就是view和model的binder連接器,通過它來實現雙向綁定。

數據綁定原理:

實現方法有這幾種:發佈者-訂閱者模式(backbone.js)髒值檢查(angular.js) 數據劫持(vue.js)

這裏簡析一下vue.js的數據綁定原理,

vue.js採用數據劫持結合發佈者-訂閱者的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變動時,發佈消息給訂閱者,觸發相應的監聽回調。通過Directives指令去對DOM做封裝,當數據發生變化,會通知指令去修改對應的DOM,數據驅動DOM的變化。vue.js還會對操作做一些監聽(DOM Listener),當我們修改視圖的時候,vue.js監聽到這些變化,從而改變數據。這樣就形成了數據的雙向綁定。

存取描述符均具有以下可選鍵值:

configurable:爲true時屬性可配置

enumerable:爲true時屬性可枚舉

get:給屬性提供getter方法,取得數據時回調的函數,調用時不傳參,傳this對象

set:給屬性提供setter方法,設置數據值時回調的函數,調用時傳入一個參數,即屬性新的值

關於Object.defineProperty:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

proxy代理器:ES6新方法,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。

觀察者模式和發佈/訂閱者模式的差異:

  • Observer模式要求觀察者必須訂閱內容改變的事件,定義了一個一對多的依賴關係;
  • Publish/Subscribe模式使用了一個主題/事件通道,這個通道介於訂閱着與發佈者之間;
  • 觀察者模式裏面觀察者「被迫」執行內容改變事件(subject內容事件);發佈/訂閱模式中,訂閱着可以自定義事件處理程序;
  • 觀察者模式兩個對象之間有很強的依賴關係;發佈/訂閱模式兩個對象之間的耦合度底。

具體實現步驟:

●Observer 對observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上setter  getter,監聽數據變化

● compile解析模板指令,將模板中的變量替換成數據,接着初始化渲染頁面視圖,並將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者。一旦數據有變動,訂閱者收到通知,就會更新視圖。

● Watcher訂閱者是Observer和Compile之間通信的橋樑,主要負責:

         1)在自身實例化時,往屬性訂閱器(Dep)裏面添加自己

         2)自身必須有一個update()方法

         3)待屬性變動,dep.notice()通知時,就調用自身的update()方法,並觸發Compile中綁定的回調

● viewModel作爲數據綁定的入口,整合Observer、Compile、Watcher三者,通過Observer來監聽自己的model數據變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋樑,達到數據變化的雙向綁定效果。

使用Object.defineProperty()來實現簡單的雙向綁定小例子。

 <div id="app">
      <input type="text" id="input">
      <span id="text"></span>
 </div>
 var obj = {};
 Object.defineProperty(obj, 'prop', {
     get: function() {
         return val;
     },
     set: function(newVal) {
         val = newVal;
         document.getElementById('input').value = val;
         document.getElementById('text').innerHTML = val;
     }
 });
 document.addEventListener('keyup', function(e) {
     obj.prop = e.target.value;
 });

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章