首頁  >  文章  >  web前端  >  淺談Vue 資料響應式原理

淺談Vue 資料響應式原理

不言
不言原創
2018-05-07 14:38:171450瀏覽

這篇文章主要介紹了關於淺談Vue 數據響應式原理,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

前言

Vue的資料回應主要是依賴了Object.defineProperty(),那麼整個過程是怎麼樣的呢?以我們自己的想法來走Vue的道路,其實也就是以Vue的原理為終點,我們來逆推一下實現過程。

本文程式碼皆為低配版本,很多地方都不嚴謹,例如if(typeof obj === 'object')這是在判斷obj是否為一個對象,雖然obj也有可能是數組等其他類型的數據,但是本文為了簡便,就直接這樣寫來表示判斷對象,對於數組使用Array.isArray()。

改造資料

我們先來嘗試寫一個函數,用來改造物件:

為什麼要先寫這個函數呢?因為改造資料是一個最基礎也是最重要的步驟,之後所有的步驟都會依賴這一步。

// 代码 1.1
function defineReactive (obj,key,val) {
 Object.defineProperty(obj,key,{
  enumerable: true,
  configurable: true,
  get: function () {
   return val;
  },
  set: function (newVal) {
   //判断新值与旧值是否相等
   //判断的后半段是为了验证新值与旧值都为NaN的情况 NaN不等于自身
   if(newVal === val || (newVal !== newVal && value !== value)){
    return ;
   }
   val = newVal;
  }
 });
}

例如const obj = {},然後再呼叫defineReactive(obj,'a',2)方法,此時在函數內,val=2 ,然後每次取得obj.a的值的時候都是取得val的值,設定obj.a的時候也是設定val的值。 (每次呼叫defineReactive都會產生一個閉包保存了val的值);

#流程討論

經過驗證之後,發現這個函數確實可以使用的。然後我們來討論一下回應的流程:

  1. 輸入資料

  2. 改造資料(defineReactive() )

  3. 如果資料變動=> 觸發事件

#我們來看第三步,資料變動如何觸發之後的事件呢?仔細思考一下,如果要改變數據,那就必須先set數據,那我們直接set()裡面加入方法就ok了呀。

然後還有一個重要問題:

#依賴收集



  1. 我們怎麼知道資料改變之後要觸發的是什麼事件呢?在Vue中:

  2. 使用資料=> 視圖; 使用了資料來渲染視圖,那麼在取得資料的時候收集依賴是最佳的時機,Vue在改造資料屬性的時候產生一個Dep實例,用於收集依賴。

// 代码 1.2
class Dep {
 constructor(){
  //订阅的信息
  this.subs = [];
 }

 addSub(sub){
  this.subs.push(sub);
 }

 removeSub (sub) {
  remove(this.subs, sub);
 }

 //此方法的作用等同于 this.subs.push(Watcher);
 depend(){
  if (Dep.target) {
   Dep.target.addDep(this);
  }
 }
 //这个方法就是发布通知了 告诉你 有改变啦
 notify(){
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
   subs[i].update();
  }
 }
}
Dep.target = null;

程式碼1.2就是Dep的部分程式碼,暫時只需要知道2個方法的作用就可以了

depend() --- 可以理解為收集依賴的事件,不考慮其他方面的話功能等同於addSub()

  1. notify() --- 這個方法更為直觀了,執行所有依賴的update()方法。就是之後的改變視圖啊 等等。

  2. 本篇主要討論資料回應的過程,不深入討論 Watcher類,所以Dep中的方法知道作用就可以了。

  3. 然後就是改變程式碼1.1了
  4. //代码 1.3
    function defineReactive (obj,key,val) {
     const dep = new Dep();
    
     Object.defineProperty(obj,key,{
      enumerable: true,
      configurable: true,
      get: function () {
       if(Dep.target){
        //收集依赖 等同于 dep.addSub(Dep.target)
        dep.depend()
       }
       return val;
      },
      set: function (newVal) {
       if(newVal === val || (newVal !== newVal && val !== val)){
        return ;
       }
       val = newVal;
       //发布改变
       dep.notify();
      }
     });
    }
  5. 這程式碼中有一個疑點,Dep.target是什麼?為什麼要有Dep.target才會收集依賴呢?
  6. Dep是一個類,Dep.target是類別的屬性,並不是dep實例的屬性。

Dep類別在全域可用,所以Dep.target在全域能存取到,可以任意改變它的值。

get這個方法使用很平常,不可能每次使用取得資料值的時候都去呼叫dep.depend()。


dep.depend()其實就是dep.addSub(Dep.target)。

那麼最好方法就是,在使用之前把Dep.target設定成某個對象,在訂閱完成之後設定Dep.target = null。


驗證

#是時候來驗證一波程式碼的可用性了

#

//代码 1.4

const obj = {};//这一句是不是感觉很熟悉 就相当于初始化vue的data ---- data:{obj:{}};

//低配的不能再低配的watcher对象(源码中是一个类,我这用一个对象代替了)
const watcher = {
 addDep:function (dep) {
  dep.addSub(this);
 },
 update:function(){
  html();
 }
}
//假装这个是渲染页面的
function html () {
 document.querySelector(&#39;body&#39;).innerHTML = obj.html;
}
defineReactive(obj,&#39;html&#39;,&#39;how are you&#39;);//定义响应式的数据

Dep.target = watcher;
html();//第一次渲染界面
Dep.target = null;

此時瀏覽器上的介面是這樣的

#然後在下開啟了控制台開始調試,輸入:

######
obj.html = &#39;I am fine thank you&#39;
#########然後就發現,按下回車的那一瞬間,奇蹟發生了,頁面變成了######## #############結尾############Vue資料回應的設計模式和訂閱發布模式有一點像,但是不同,每一個dep實例就是一個訂閱中心,每次發布都會把所有的訂閱全部發佈出去。 ###

Vue的響應式原理其實還有很大一部分,本文主要討論了Vue是如何讓數據進行響應,但是實際上,一般的數據都是很多的,一個數據被多處使用,改變數據之後觀察新價值,如何觀察、如何訂閱、如何調度,都還有很大一部分沒有討論。主要的三個類別Dep(收集依賴)、Observer(觀察資料)、Watcher(訂閱者,若資料有變化通知訂閱者),都只提了一點點。

之前寫有一篇Vue響應式----陣列變異方法,針對Vue中對陣列的改造進行討論。當然之後有更多其他的文章,整個資料回應流程還有很多內容,三個主要的類別都還沒討論完。

其實閱讀原始碼不只是為了知道原始碼是如何運作的,更重要的是學習作者的想法與方法,我寫的文章都不長,希望自己能夠每次專注於一個點,能夠真真實實領悟到這一點的原理。當然也想控制閱讀時間,免得大家看到一半就關閉了。

相關推薦:

vue資料傳遞方法總結

vue資料傳遞實作步驟詳解

Vue資料監聽方法watch的使用

#

以上是淺談Vue 資料響應式原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn