1. 程式人生 > >vue開發:vue,angular,react資料雙向繫結原理分析

vue開發:vue,angular,react資料雙向繫結原理分析

傳統做法

前端維護狀態,手動操作DOM更新檢視。前端框架對伺服器資料通過模版進行渲染。當用戶產生了一個動作之後,我們通過document.getElementBy... 手動進行DOM更新。 
框架幫忙分離資料和檢視,後續狀態更新需要手動操作DOM,因為框架只管首次渲染,不追蹤狀態監聽變化。

雙向資料繫結

當我們在前端開發中採用MV*的模式時,M - model,指的是模型,也就是資料,V - view,指的是檢視,也就是頁面展現的部分。通常,我們需要編寫程式碼,將從伺服器獲取的資料進行“渲染”,展現到檢視上。每當資料有變更時,我們會再次進行渲染,從而更新檢視,使得檢視與資料保持一致。

這裡寫圖片描述

頁面也會通過使用者的互動,產生狀態、資料的變化,這個時候,我們則編寫程式碼,將檢視對資料的更新同步到資料,以致於同步到後臺伺服器。也就是

這裡寫圖片描述

不同的前端 MV* 框架對於這種 Model 和 View 間的資料同步有不同的處理。在 Backbone 中,Model 到 View 的資料傳遞,可以在 View 中監聽 Model 的 change 事件,每當 Model 更新,View 中重新執行 render。而 View 到 Model 的資料傳遞,可以監聽 View 對應的 DOM 元素的各種事件,在檢測到 View 狀態變更後,將變更的資料傳送到 Model(通過兩邊的監聽事件)。相較於 Backbone,

AngularJS 所代表的 MVVM 框架則更進一步,從框架層面支援這種資料同步機制,而且是雙向資料繫結:

這裡寫圖片描述

在不同的 MVVM 框架中,實現雙向資料繫結的技術有所不同。

AngularJS 採用“髒值檢測”的方式,資料發生變更後,對於所有的資料和檢視的繫結關係進行一次檢測,識別是否有資料發生了改變,有變化進行處理,可能進一步引發其他資料的改變,所以這個過程可能會迴圈幾次,一直到不再有資料變化發生後,將變更的資料傳送到檢視,更新頁面展現。如果是手動對 ViewModel 的資料進行變更,為確保變更同步到檢視,需要手動觸發一次“髒值檢測”。

VueJS 則使用 ES5 提供的 Object.defineProperty() 方法,監控對資料的操作,從而可以自動觸發資料同步。並且,由於是在不同的資料上觸發同步,可以精確的將變更傳送給繫結的檢視,而不是對所有的資料都執行一次檢測。

Vue 雙向資料繫結實現

資料與檢視的繫結與同步,最終體現在對資料的讀寫處理過程中,也就是 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 reactGetter(){
               if(Dep.target){
                   dep.depend();
               }
               return value;
           },
           set: function reactSetter(newVal){
               if (value === newVal) {
                   return;
               } else {
                    value = newVal;
                    //如果資料發生改變,則通知所有的 watcher(藉助 dep.notify())
                    dep.notify();
               }
           }
       })
   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在對資料進行讀取時,如果當前有 Watcher(對資料的觀察者,watcher 會負責將獲取的新資料傳送給檢視),那將該 Watcher 繫結到當前的資料上(dep.depend(),dep 關聯當前資料和所有的 watcher 的依賴關係),是一個檢查並記錄依賴的過程。而在對資料進行賦值時,如果資料發生改變,則通知所有的 watcher(藉助 dep.notify())。這樣,即便是我們手動改變了資料,框架也能夠自動將資料同步到檢視。

資料繫結關係的識別過程

Vue 和 AngularJS 中,都是通過在 HTML 中新增指令的方式,將檢視元素與資料的繫結關係進行宣告

   <form id="test">
       <input type="text" v-model="name">
   </form>
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

以上的 HTML 程式碼表示該 input 元素與 name 資料進行繫結。在 JS 程式碼中可以這樣進行初始化:

   var vm = new Vue({
      el: '#test',
      data: {
        name: 'sysuzhyupeng'
      }
    })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

程式碼正確執行後,頁面上 input 元素對應的位置會顯示上面程式碼中給出的初始值:sysuzhyupeng。 
執行 vm.name = ‘zhyupeng’ 後,頁面上 input 也會更新為顯示: zhyupeng。在頁面文字框中修改內容為:yupeng,則通過vm.name 獲取的值為:’yupeng’

React資料繫結

React採用這種方式,考慮虛擬DOM樹的更新:

  • 屬性更新,元件自己處理
  • 結構更新,重新“渲染”子樹(虛擬DOM),找出最小改動步驟,打包DOM操作,給真實DOM樹打補丁

單純從資料繫結來看,React虛擬DOM沒有資料繫結,因為setState()不維護上一個狀態(狀態丟棄),談不上繫結

從資料更新機制來看,React類似於提供資料模型的方式(必須通過state更新)

沒有雙向資料繫結的話,input的雙向場景要怎麼實現?通過框架提供的API,手動通知資料變化,和操作DOM的方式很像