一文了解vue中watcher資料雙向繫結原理(附程式碼)

funnyok發表於2021-09-11

之前的文章《》中,給大家瞭解了vue中observer資料雙向繫結原理。下面本篇文章給大家瞭解vue中watcher資料雙向繫結原理,一定的參考價值,有需要的朋友可以參考一下。

一文了解vue中watcher資料雙向繫結原理(附程式碼)

vue資料雙向繫結原理,和簡單的實現,本文將實現mvvmwatcher

微信截圖_20210823095546.jpg

1)

2)

3)

vue資料雙向繫結原理,和簡單的實現,本文將實現mvvmWatcher

上面的步驟已經實現了監聽器,和訂閱器,當屬性發生改變,發出通知,那麼這個通知是通知誰呢,肯定是訂閱者watcher.Watcher訂閱者作為ObserverCompile之間通訊的橋樑,主要做的事情是:

1、在自身例項化時往屬性訂閱器(dep)裡面新增自己

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

3、待屬性變動dep.notice()通知時,能呼叫自身的update()方法,並觸發Compile中繫結的回撥,則釋放自己。

// Watcher
function Watcher(vm, exp, cb) {
  this.cb = cb;
  this.$vm = vm;
  this.exp = exp;
  // 此處為了觸發屬性的getter,從而在dep新增自己,結合Observer更易理解
  this.value = this.get(); // 將自己新增到訂閱器的操作
}
Watcher.prototype = {
  update: function () {
    this.run(); // 屬性值變化收到通知
  },
  run: function () {
    var value = this.get(); // 取到最新值
    var oldVal = this.value;
    if (value !== oldVal) {
      this.value = value;
      this.cb.call(this.$vm, value, oldVal); // 執行Compile中繫結的回撥,更新檢視
    }
  },
  get: function () {
    Dep.target = this; // 將當前訂閱者指向自己, 快取
    var value = this.$vm[this.exp]; // 強制觸發監聽的getter,新增自己到屬性訂閱器中
    Dep.target = null; // 新增完畢,重置釋放
    return value;
  },
};

訂閱者要快取自己,並且告訴監聽器,要把我加到訂閱器裡面去。所以還要改造下監聽器

function defineReactive(data, key, val) {
  var dep = new Dep()
  observe(val); // 監聽子屬性
  Object.defineProperty(data, key, {
    ....
    get: function() {
      // 由於需要在閉包內新增watcher,所以可以在Dep定義一個全域性target屬性,暫存watcher, 新增完移除
      Dep.target && dep.addDep(Dep.target);
      return val;
    },
    ....
  });

}

例項化Watcher的時候,呼叫get()方法,透過Dep.target=watcherInstance標記訂閱者是當前watcher例項,強行觸發屬性定義的getter方法,getter方法執行的時候,就會在屬性的訂閱器dep新增當前watcher例項,從而在屬性值有變化的時候watcherInstance就能收到更新通知。

實現MVVM

到這兒先將監聽器Observer和監聽者Watcher連起來,先模擬一些資料,實現簡單的資料繫結

<div id="name"></div>
<script>
  function Vue(data, el, exp) {
    this.data = data;
    observe(data);
    el.innerHTML = this.data[exp]; // 初始化模板資料的值
    new Watcher(this, exp, function (value) {
      el.innerHTML = value;
    });
    return this;
  }
  var ele = document.querySelector("#name");
  var vue = new Vue(
    {
      name: "hello world",
    },
    ele,
    "name"
  );
  setInterval(function () {
    vue.data.name = "chuchur " + new Date() * 1;
  }, 1000);
</script>

這可以看到div的和內容初始為hello world,每隔一秒之後變換為chuchur加時間戳,雖然是實現了,但是與想象的還差很多。是vue.name不是vue.data.name,所以這裡需要給Vue例項新增一個屬性代理的方法,使訪問vm的屬性代理為訪問vm.data的屬性,改造後的程式碼如下:

function Vue(options) {

  this.$options = options || {};
  this.data = this.$options.data;
  // 屬性代理,實現 vm.xxx -> vm.data.xxx
  var self = this;
  Object.keys(this.data).forEach(function(key) {

    self.proxy(key); // 繫結代理屬性

  });
  observe(this.data, this);
  el.innerHTML = this.data[exp]; // 初始化模板資料的值
  new Watcher(this, exp, function(value) {

    el.innerHTML = value;

  });
  return this;

}

Vue.prototype = {

  proxy: function(key) {
    var self = this;
    Object.defineProperty(this, key, {
      enumerable: false,
      configurable: true,
      get: function proxyGetter() {
        return self.data[key];
      },
      set: function proxySetter(newVal) {
        self.data[key] = newVal;
      }
    });
  }

}

然後就可以透過vue.name,直接改版模板的資料了,下一步就要實現解析器Complie

[完]

推薦學習:

以上就是一文了解vue中watcher資料雙向繫結原理(附程式碼)的詳細內容,更多請關注php中文網其它相關文章!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2827395/,如需轉載,請註明出處,否則將追究法律責任。

相關文章