100行代碼帶你玩Vue相應式

Vue相應式道理

《100行代碼帶你玩Vue相應式》

看過vue官方文檔的同硯,對這張圖應當已然相稱熟習了。

vue的相應式是怎樣完成的?

聽過太多回覆,經由歷程Object.defineProperty,然則再細緻的問時,對方渾然不知。

先擼為敬

const Observer = function(data) {
  for (let key in data) {
    defineReactive(data, key);
  }
}

const defineReactive = function(obj, key) {
  const dep = new Dep();
  let val = obj[key];
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log('in get');
      dep.depend();
      return val;
    },
    set(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      dep.notify();
    }
  });
}

const observe = function(data) {
  return new Observer(data);
}

const Vue = function(options) {
  const self = this;
  if (options && typeof options.data === 'function') {
    this._data = options.data.apply(this);
  }

  this.mount = function() {
    new Watcher(self, self.render);
  }

  this.render = function() {
    with(self) {
      _data.text;
    }
  }

  observe(this._data);  
}

const Watcher = function(vm, fn) {
  const self = this;
  this.vm = vm;
  Dep.target = this;
  
  this.addDep = function(dep) {
    dep.addSub(self);
  }

  this.update = function() {
    console.log('in watcher update');
    fn();
  }

  this.value = fn();
  Dep.target = null;
}

const Dep = function() {
  const self = this;
  this.target = null;
  this.subs = [];
  this.depend = function() {
    if (Dep.target) {
      Dep.target.addDep(self);
    }
  }

  this.addSub = function(watcher) {
    self.subs.push(watcher);
  }

  this.notify = function() {
    for (let i = 0; i < self.subs.length; i += 1) {
      self.subs[i].update();
    }
  }
}

const vue = new Vue({
  data() {
    return {
      text: 'hello world'
    };
  }
})

vue.mount(); // in get
vue._data.text = '123'; // in watcher update /n in get

這裏我們用不到100行的代碼,完成了一個淺易的vue相應式。固然,這裏假如不斟酌時期的歷程,我置信,40行代碼以內能夠搞定。然則我這裏不想省略,為何呢?我怕你把个中的歷程自動疏忽掉,怕他人問你相干東西的時刻,明顯本身看過了,卻被懟的張口結舌。總之,我是為了你好,多喝熱水。

Dep的作用是什麼?

依靠收集器,這不是官方的名字蛤,我本身起的,為了好記。

用兩個例子來看看依靠收集器的作用吧。

  • 例子1,毫無意義的襯着是否是沒必要?

    const vm = new Vue({
        data() {
            return {
                text: 'hello world',
                text2: 'hey',
            }
        }
    })

    vm.text2的值發作變化時,會再次挪用render,而template中卻沒有運用text2,所以這裏處置懲罰render是否是毫無意義?

    針對這個例子還記得我們上面模仿完成的沒,在Vuerender函數中,我們挪用了本次襯着相干的值,所以,與襯着無關的值,並不會觸發get,也就不會在依靠收集器中添加到監聽(addSub要領不會觸發),縱然挪用set賦值,notify中的subs也是空的。OK,繼承回歸demo,來一小波測試去印證下我說的吧。

    const vue = new Vue({
      data() {
        return {
          text: 'hello world',
          text2: 'hey'
        };
      }
    })
    
    vue.mount(); // in get
    vue._data.text = '456'; // nothing
    vue._data.text2 = '123'; // in watcher update /n in get
  • 例子2,多個Vue實例援用同一個data時,關照誰?是否是應當倆都關照?

    
    let commonData = {
      text: 'hello world'
    };
    
    const vm1 = new Vue({
      data() {
        return commonData;
      }
    })
    
    const vm2 = new Vue({
      data() {
        return commonData;
      }
    })
    
    vm1.mount(); // in get
    vm2.mount(); // in get
    commonData.text = 'hey' // 輸出了兩次 in watcher update /n in get

    老例子,本身代入進去嘗嘗。

願望經由歷程這兩個例子,你已也許清晰了Dep的作用,有無原來就那麼回事的覺得?有就對了。總結一下吧(以下依靠收集器實為Dep):

  • vuedata初始化為一個Observer並對對象中的每一個值,重寫了个中的getsetdata中的每一個key,都有一個自力的依靠收集器。
  • get中,向依靠收集器添加了監聽
  • 在mount時,實例了一個Wathcer,將收集器的目的指向了當前Watcher
  • data值發作變動時,觸發set,觸發了依靠收集器中的一切監聽的更新,來觸發Watcher.update
    原文作者:JserWang
    原文地址: https://segmentfault.com/a/1190000014580469
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞