欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue收集依賴與觸發(fā)依賴源碼刨析

 更新時(shí)間:2022年10月21日 14:44:16   作者:Young soul2  
vue對(duì)依賴的管理使用的是發(fā)布訂閱者模式,其中watcher扮演訂閱者,Dep扮演發(fā)布者。所以dep中會(huì)有多個(gè)watcher,一個(gè)訂閱者也可以有多個(gè)發(fā)布者(依賴)??偣踩齻€(gè)過程:定義依賴、收集依賴、觸發(fā)依賴。下面開始詳細(xì)講解三個(gè)過程

定義依賴

定義依賴是什么時(shí)候開始的呢?通過源碼可以發(fā)現(xiàn)在執(zhí)行_init函數(shù)的時(shí)候會(huì)執(zhí)行initState(vm)方法:

function initState(vm) {
      ...
      if (opts.data) {
          initData(vm);
      }
      else {
          var ob = observe((vm._data = {}));
          ob && ob.vmCount++;
      }
      ...
  }

先觸發(fā)initData方法:

function initData(vm) {
      var data = vm.$options.data;
      data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
      ...
      var keys = Object.keys(data);
      ...
      var i = keys.length;
      while (i--) {
          var key = keys[i];
          {
              if (methods && hasOwn(methods, key)) {
                  warn$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);
              }
          }
          if (props && hasOwn(props, key)) {
              warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +
                      "Use prop default value instead.", vm);
          }
          else if (!isReserved(key)) {
              proxy(vm, "_data", key);
          }
      }
      // observe data
      var ob = observe(data);
      ob && ob.vmCount++;
  }

首先會(huì)獲取data數(shù)據(jù),然后執(zhí)行proxy(vm, “_data”, key):

var sharedPropertyDefinition = {
      enumerable: true,
      configurable: true,
      get: noop,
      set: noop
  };
function proxy(target, sourceKey, key) {
      sharedPropertyDefinition.get = function proxyGetter() {
          return this[sourceKey][key];
      };
      sharedPropertyDefinition.set = function proxySetter(val) {
          this[sourceKey][key] = val;
      };
      Object.defineProperty(target, key, sharedPropertyDefinition);
  }

在vm實(shí)例中添加了_data對(duì)象并將data的數(shù)據(jù)給了_data。隨后執(zhí)行observe(data):

function observe(value, shallow, ssrMockReactivity) {
      ...
      else if (shouldObserve &&
          (ssrMockReactivity || !isServerRendering()) &&
          (isArray(value) || isPlainObject(value)) &&
          Object.isExtensible(value) &&
          !value.__v_skip /* ReactiveFlags.SKIP */) {
          ob = new Observer(value, shallow, ssrMockReactivity);
      }
      return ob;
  }

主要執(zhí)行 new Observer(value, shallow, ssrMockReactivity)方法:

function Observer(value, shallow, mock) {
          if (shallow === void 0) { shallow = false; }
          if (mock === void 0) { mock = false; }
          this.value = value;
          this.shallow = shallow;
          this.mock = mock;
          // this.value = value
          this.dep = mock ? mockDep : new Dep();
          this.vmCount = 0;
          def(value, '__ob__', this);
          if (isArray(value)) {
             ...
          }
          else {
              /**
               * Walk through all properties and convert them into
               * getter/setters. This method should only be called when
               * value type is Object.
               */
              var keys = Object.keys(value);
              for (var i = 0; i < keys.length; i++) {
                  var key = keys[i];
                  defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);
              }
          }
      }

主要執(zhí)行defineReactive:

function defineReactive(obj, key, val, customSetter, shallow, mock) {
      var dep = new Dep();
      var property = Object.getOwnPropertyDescriptor(obj, key);
      if (property && property.configurable === false) {
          return;
      }
      // cater for pre-defined getter/setters
      var getter = property && property.get;
      var setter = property && property.set;
      if ((!getter || setter) &&
          (val === NO_INIITIAL_VALUE || arguments.length === 2)) {
          val = obj[key];
      }
      var childOb = !shallow && observe(val, false, mock);
      Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get: function reactiveGetter() {
              var value = getter ? getter.call(obj) : val;
              if (Dep.target) {
                  {
                      dep.depend({
                          target: obj,
                          type: "get" /* TrackOpTypes.GET */,
                          key: key
                      });
                  }
                  if (childOb) {
                      childOb.dep.depend();
                      if (isArray(value)) {
                          dependArray(value);
                      }
                  }
              }
              return isRef(value) && !shallow ? value.value : value;
          },
          set: function reactiveSetter(newVal) {
              var value = getter ? getter.call(obj) : val;
              if (!hasChanged(value, newVal)) {
                  return;
              }
              if (customSetter) {
                  customSetter();
              }
              if (setter) {
                  setter.call(obj, newVal);
              }
              else if (getter) {
                  // #7981: for accessor properties without setter
                  return;
              }
              else if (!shallow && isRef(value) && !isRef(newVal)) {
                  value.value = newVal;
                  return;
              }
              else {
                  val = newVal;
              }
              childOb = !shallow && observe(newVal, false, mock);
              {
                  dep.notify({
                      type: "set" /* TriggerOpTypes.SET */,
                      target: obj,
                      key: key,
                      newValue: newVal,
                      oldValue: value
                  });
              }
          }
      });
      return dep;
  }

可以看出新增了一個(gè)依賴對(duì)象Dep,表示是該數(shù)據(jù)被哪些組件所依賴,并定義了data下數(shù)據(jù)的get和set方法。

收集依賴

vue是怎么收集依賴的呢?當(dāng)組件渲染的時(shí)候會(huì)執(zhí)行下面的渲染函數(shù):

var render = function render() {
  var _vm = this,
    _c = _vm._self._c
  return _c("div", [
    _vm._v("\n  " + _vm._s(_vm.num) + "\n  " + _vm._s(_vm.a) + "\n  "),
    _c("button", { on: { click: _vm.addModule } }, [_vm._v("新增")]),
  ])
}

原內(nèi)容如下:

<template>
  <div>
    {{num}}
    {{a}}
    <button @click="addModule">新增</button>
  </div>
</template>
<script>
export default {
  name: "TestWebpackTest",
  mounted() {
    console.log(this);
  },
  data() {
    return {
      num: 1,
      a:2
    };
  },
  computed:{
    getNum(){
      return this.num+Math.random()
    },
    getA(){
      return this.a+Math.random()
    }
  },  
  methods: {
    addModule() {
      this.num++;
    }
  }
};
</script>
<style lang="scss">
div {
  .test {
    width: 10px;
    height: 15px;
    background-color: blue;
  }
}
</style>

在渲染組件模版的時(shí)候會(huì)取獲取數(shù)據(jù),此時(shí)會(huì)觸發(fā)data中定義數(shù)據(jù)的getter方法,此時(shí)為當(dāng)前掛載組件實(shí)例化watcher的時(shí)候會(huì)設(shè)置Dep.target。

Dep.prototype.depend = function (info) {
     if (Dep.target) {
          Dep.target.addDep(this);
          if (info && Dep.target.onTrack) {
              Dep.target.onTrack(__assign({ effect: Dep.target }, info));
          }
      }
  };

通知當(dāng)前依賴的組件去添加依賴,當(dāng)前依賴的組件會(huì)將該依賴添加進(jìn)入newDeps。相反依賴也可能被多個(gè)組件使用,所以在該依賴也有多個(gè)組件。

Watcher.prototype.addDep = function (dep) {
      var id = dep.id;
      if (!this.newDepIds.has(id)) {
          this.newDepIds.add(id);
          this.newDeps.push(dep);
          if (!this.depIds.has(id)) {
              dep.addSub(this);
          }
      }
  };

其實(shí)本質(zhì)就是建立組件和變量之間的依賴關(guān)系,一個(gè)組件可以有多個(gè)依賴,一個(gè)依賴可以被多個(gè)組件使用。

觸發(fā)依賴

當(dāng)數(shù)據(jù)發(fā)生變化時(shí)會(huì)觸發(fā)數(shù)據(jù)的gettter方法:

dep.notify({
    type: "set" /* TriggerOpTypes.SET */,
    target: obj,
    key: key,
    newValue: newVal,
    oldValue: value
});

調(diào)用當(dāng)前依賴的notify方法去通知組件更新:

Dep.prototype.notify = function (info) {
 // stabilize the subscriber list first
  var subs = this.subs.slice();
...
  for (var i = 0, l = subs.length; i < l; i++) {
      if (info) {
          var sub = subs[i];
          sub.onTrigger &&
              sub.onTrigger(__assign({ effect: subs[i] }, info));
      }
      subs[i].update();
  }
};

該方法就是獲取當(dāng)前依賴下的組件并調(diào)用該組件的update方法:

Watcher.prototype.update = function () {
   /* istanbul ignore else */
    if (this.lazy) {
        this.dirty = true;
    }
    else if (this.sync) {
        this.run();
    }
    else {
        queueWatcher(this);
    }
};

下面的內(nèi)容我在另外一篇文章中講過:vue2.7.10在數(shù)據(jù)發(fā)生變化后是如何更新頁面的。

總結(jié)

  • 定義依賴是在實(shí)例化組件的時(shí)候執(zhí)行的此時(shí)的Dep.target指向當(dāng)前vm實(shí)例,在initData方法中會(huì)遍歷data數(shù)據(jù)并設(shè)置get和set方法,每個(gè)數(shù)據(jù)都有一個(gè)dep(依賴),表示每個(gè)數(shù)據(jù)都會(huì)被多個(gè)組件所依賴。
  • 收集依賴是在執(zhí)行render方法的時(shí)候。該方法觸發(fā)數(shù)據(jù)的get方法,建立數(shù)據(jù)的dep(依賴)和watcher之間的聯(lián)系。
  • 觸發(fā)依賴是在數(shù)據(jù)發(fā)生改變的時(shí)候執(zhí)行。此時(shí)會(huì)觸發(fā)數(shù)據(jù)的set方法,取出當(dāng)前數(shù)據(jù)的依賴者(watcher)并循環(huán)調(diào)用依賴者的update方法更新視圖。

到此這篇關(guān)于Vue收集依賴與觸發(fā)依賴源碼刨析的文章就介紹到這了,更多相關(guān)Vue收集依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論