Vue是如何實現雙向綁定的

vue的雙向綁定指的是數據變化更新視圖,視圖變化更新數據。
視圖變化更新數據一般就採用事件監聽的方式就可以了,數據變化更新視圖就需要涉及響應式原理。

vue2.x的響應式原理的基礎是Object.defineProperty屬性。利用Object.defineProperty劫持對象的訪問器,在屬性值發生變化時我們可以獲取變化,然後根據變化進行後續響應,在vue3.0中通過Proxy代理對象進行類似的操作。

Vue三要素

雙向綁定其實已經是一個老掉牙的問題了,只要涉及到MVVM框架就不得不談的知識點,但它畢竟是Vue的三要素之一。

  • 響應式: 例如如何監聽數據變化,其中的實現方法就是我們提到的雙向綁定
  • 模板引擎: 如何解析模板
  • 渲染: Vue如何將監聽到的數據變化和解析後的HTML進行渲染

可以實現雙向綁定的方法有很多,KnockoutJS基於觀察者模式的雙向綁定,Ember基於數據模型的雙向綁定,Angular基於髒檢查的雙向綁定,最後就是基於數據劫持的雙向綁定。

常見的基於數據劫持的雙向綁定有兩種實現,一個是目前Vue在用的Object.defineProperty,另一個是ES2015中新增的Proxy

基於數據劫持實現的雙向綁定的特點

1.什麼是數據劫持

含義:利用Object.defineProperty劫持對象的訪問器,在屬性值發生變化時我們可以獲取變化,從而進行進一步操作。

// 這是將要被劫持的對象
const data = {
  name: '',
};

function say(name) {
  if (name === '古天樂') {
    console.log('給大家推薦一款超好玩的遊戲');
  } else if (name === '渣渣輝') {
    console.log('戲我演過很多,可遊戲我只玩貪玩懶月');
  } else {
    console.log('來做我的兄弟');
  }
}

// 遍歷對象,對其屬性值進行劫持
Object.keys(data).forEach(function(key) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get');
    },
    set: function(newVal) {
      // 當屬性值發生變化時我們可以進行額外操作
      console.log(`大家好,我係${newVal}`);
      say(newVal);
    },
  });
});

data.name = '渣渣輝';
//大家好,我係渣渣輝
//戲我演過很多,可遊戲我只玩貪玩懶月

2.數據劫持的優勢

目前業界分爲兩個大的流派,一個是以React爲首的單向數據綁定,另一個是以Angular、Vue爲主的雙向數據綁定。

其實三大框架都是既可以雙向綁定也可以單向綁定。
比如React可以手動綁定onChange和value實現雙向綁定,也可以調用一些雙向綁定庫;Vue也加入了props這種單向流的api實現單向綁定。

  • 無需顯示調用

例如Vue運用數據劫持+發佈訂閱,直接可以通知變化並驅動視圖。
上面的例子也是比較簡單的實現data.name = '渣渣輝'後直接觸發變更;而比如Angular的髒檢測則需要顯示調用markForCheck(可以用zone.js避免顯示調用,不展開),react需要顯示調用setState。

  • 可精確得知變化數據

還是上面的小例子,我們劫持了屬性的setter,當屬性值改變,我們可以精確獲知變化的內容newVal。因此在這部分不需要額外的diff操作,否則我們只知道數據發生了變化而不知道具體哪些數據變化了,這個時候需要大量diff來找出變化值,這是額外性能損耗。

3 基於數據劫持雙向綁定的實現思想

基於數據劫持的雙向綁定離不開Proxy與Object.defineProperty等方法對對象/對象屬性的"劫持",我們要實現一個完整的雙向綁定需要以下幾個要點。

  1. 利用Proxy或Object.defineProperty生成的Observer針對對象/對象的屬性進行"劫持",在屬性發生變化後通知訂閱者
  2. 解析器Compile解析模板中的Directive(指令),收集指令所依賴的方法和數據,等待數據變化然後進行渲染
  3. Watcher屬於Observer和Compile橋樑,它將接收到的Observer產生的數據變化,並根據Compile提供的指令進行視圖渲染,使得數據變化促使視圖變化
    在這裏插入圖片描述

Proxy與Object.defineProperty的優劣對比

  • Proxy可以直接監聽對象而非屬性
  • Proxy可以直接監聽數組的變化
  • Proxy有多達13種攔截方法,不限於apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具備的
  • Proxy返回的是一個新對象,我們可以只操作新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改
  • Proxy作爲新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利
  • Object.defineProperty兼容IE

Vue的響應式系統

在這裏插入圖片描述
響應式系統簡述:

  • 任何一個 Vue Component 都有一個與之對應的 Watcher 實例。
  • Vue 的 data 上的屬性會被添加 getter 和 setter 屬性。
  • 當 Vue Component render 函數被執行的時候, data 上會被 觸碰(touch), 即被讀, getter 方法會被調用, 此時 Vue 會去記錄此 Vue component 所依賴的所有 data。(這一過程被稱爲依賴收集)
  • data 被改動時(主要是用戶操作), 即被寫, setter 方法會被調用, 此時 Vue 會去通知所有依賴於此 data 的組件去調用他們的 render 函數進行更新。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章