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

Vue computed實(shí)現(xiàn)原理深入講解

 更新時(shí)間:2022年10月21日 15:09:52   作者:Young soul2  
computed又被稱(chēng)作計(jì)算屬性,用于動(dòng)態(tài)的根據(jù)某個(gè)值或某些值的變化,來(lái)產(chǎn)生對(duì)應(yīng)的變化,computed具有緩存性,當(dāng)無(wú)關(guān)值變化時(shí),不會(huì)引起computed聲明值的變化。產(chǎn)生一個(gè)新的變量并掛載到vue實(shí)例上去

從vue(v2.7.10)源碼分析vue是如何收集依賴和觸發(fā)依賴這篇文章中我們講了vue是怎么收集依賴的。其中computed的實(shí)現(xiàn)原理和這個(gè)密切相關(guān),接下來(lái)我們看看computed的實(shí)現(xiàn)原理。

基礎(chǔ)代碼如下:

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

定義依賴

直接上代碼,在執(zhí)行render函數(shù)實(shí)例化TestWebpackTest組件的時(shí)候會(huì)執(zhí)行下面的方法:

Vue.extend = function (extendOptions) {
    ...
    if (Sub.options.computed) {
        initComputed(Sub);
    }
    ...
};

在該方法中會(huì)執(zhí)行initComputed方法,該方法遍歷computed并執(zhí)行defineComputed方法:

function initComputed(Comp) {
    var computed = Comp.options.computed;
    for (var key in computed) {
        defineComputed(Comp.prototype, key, computed[key]);
    }
}
var sharedPropertyDefinition = {
      enumerable: true,
      configurable: true,
      get: noop,
      set: noop
  };
// key: "getA"  target: Vue {constructor: ?} userDef:? getA()
function defineComputed(target, key, userDef) {
      var shouldCache = !isServerRendering(); // true
      if (isFunction(userDef)) {
          sharedPropertyDefinition.get = shouldCache
              ? createComputedGetter(key)
              : createGetterInvoker(userDef);
          sharedPropertyDefinition.set = noop;
      }
      else {
         ...
      }
     ...
      Object.defineProperty(target, key, sharedPropertyDefinition);
  }

主要執(zhí)行了createComputedGetter方法設(shè)置computed函數(shù)getA的get方法:

// key: "getA"
function createComputedGetter(key) {
      return function computedGetter() {
          var watcher = this._computedWatchers && this._computedWatchers[key];
          if (watcher) {
              if (watcher.dirty) {
                  watcher.evaluate();
              }
              if (Dep.target) {
                  if (Dep.target.onTrack) {
                      Dep.target.onTrack({
                          effect: Dep.target,
                          target: this,
                          type: "get" /* TrackOpTypes.GET */,
                          key: key
                      });
                  }
                  watcher.depend();
              }
              return watcher.value;
          }
      };
  }

該方法執(zhí)行過(guò)程在收集依賴部分分析。返回該函數(shù)后執(zhí)行Object.defineProperty(target, key, sharedPropertyDefinition),給當(dāng)前的vm設(shè)置getA響應(yīng)式。至此設(shè)置響應(yīng)完畢。

收集依賴

收集依賴發(fā)生在執(zhí)行組件渲染過(guò)程,會(huì)通過(guò)_vm.getA觸發(fā)computed的get方法。

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

會(huì)獲取當(dāng)前的computed函數(shù)生成的watcher,繼續(xù)執(zhí)行watcher.evaluate()方法:

var watcher = this._computedWatchers && this._computedWatchers[key];
 if (watcher) {
     if (watcher.dirty) { // true
         watcher.evaluate();
     }
     if (Dep.target) {
         if (Dep.target.onTrack) {
             Dep.target.onTrack({
                 effect: Dep.target,
                 target: this,
                 type: "get" /* TrackOpTypes.GET */,
                 key: key
             });
         }
         watcher.depend();
     }
     return watcher.value;
 }
Watcher.prototype.evaluate = function () {
     this.value = this.get();
     this.dirty = false;
 };

調(diào)用當(dāng)前watcher的get方法,首先設(shè)置當(dāng)前的Dep.target為當(dāng)前的watcher,然后執(zhí)行this.getter方法,該方法為getA方法,相當(dāng)于執(zhí)行該函數(shù)。

Watcher.prototype.get = function () {
     pushTarget(this);
     var value;
     var vm = this.vm;
     try {
         value = this.getter.call(vm, vm);
     }
     catch (e) {
        ...
     }
     finally {
         // "touch" every property so they are all tracked as
         // dependencies for deep watching
         if (this.deep) {
             traverse(value);
         }
         popTarget();
         this.cleanupDeps();
     }
     return value;
 };

由于依賴于a變量,所以會(huì)觸發(fā)a變量的get方法,并執(zhí)行dep.depend方法

var value = getter ? getter.call(obj) : val;
if (Dep.target) {
     {
         dep.depend({
             target: obj,
             type: "get" /* TrackOpTypes.GET */,
             key: key
         });
     }
     ...
 }
 return isRef(value) && !shallow ? value.value : value;
 ...
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));
        }
    }
};
...
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);
        }
    }
};

當(dāng)前的Dep.target是getA的watcher實(shí)例,a變量會(huì)找到訂閱者(getA的watcher),并在該watcher的newDeps里添加該變量的dep(this.newDeps.push(dep)),然后該變量會(huì)把getA的watcher加入自己的依賴(dep.addSub(this))。從而建立了getA和num之間的聯(lián)系。接下來(lái)執(zhí)行popTarget()和this.cleanupDeps()將當(dāng)前Dep.target置為組件的watcher,然后newDeps的值賦給deps。至此watcher.evaluate()執(zhí)行完后this.getter.call(vm, vm)執(zhí)行完畢,返回a的value。

觸發(fā)依賴

當(dāng)變量發(fā)生變化時(shí)會(huì)觸發(fā)該變量的set方法:

function reactiveSetter(newVal) {
    var value = getter ? getter.call(obj) : val;
     ...
     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
         });
     }
 }

主要執(zhí)行dep.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++) {
        ...
        subs[i].update();
    }
};
Watcher.prototype.update = function () {
   /* istanbul ignore else */
    if (this.lazy) {
        this.dirty = true;
    }
    else if (this.sync) {
        this.run();
    }
    else {
        queueWatcher(this);
    }
};

主要執(zhí)行subs[i].update()方法,當(dāng)前的變量a有兩個(gè)dep,computed的lazy為true不會(huì)繼續(xù)執(zhí)行。第二個(gè)dep為組件的watcher,執(zhí)行該watcher的update方法重新渲染刷新頁(yè)面。

總結(jié)

  • 在組件實(shí)例化的時(shí)候會(huì)遍歷computed設(shè)置computed的get方法并設(shè)置Dep.target為當(dāng)前computed的watcher。
  • 在執(zhí)行渲染模板方法的時(shí)候會(huì)觸發(fā)該computed的get方法。
  • 會(huì)執(zhí)行computed函數(shù),當(dāng)該函數(shù)里獲取變量時(shí)會(huì)觸發(fā)變量的get方法。該變量會(huì)通過(guò)Dep.target獲取當(dāng)前的watcher并添加自己的dep(相當(dāng)于記錄了訂閱了哪些變量),也就是說(shuō)獲取變量的時(shí)候會(huì)訂閱該變量,該變量也會(huì)在自己的依賴dep添加watcher(記錄訂閱者,發(fā)生變化時(shí)會(huì)通知訂閱者)。
  • 當(dāng)前變量發(fā)生改變時(shí)會(huì)循環(huán)觸發(fā)該變量的dep的update方法刷新頁(yè)面。其中computed方法由于已經(jīng)獲取最新值所以只需要執(zhí)行組件的update方法。

到此這篇關(guān)于Vue computed實(shí)現(xiàn)原理深入講解的文章就介紹到這了,更多相關(guān)Vue computed內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue的computed計(jì)算屬性你了解嗎

    Vue的computed計(jì)算屬性你了解嗎

    這篇文章主要為大家詳細(xì)介紹了Vue的computed計(jì)算屬性,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • Vue如何清空對(duì)象

    Vue如何清空對(duì)象

    這篇文章主要介紹了Vue如何清空對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Vue混合文件使用以及ref的引用實(shí)例詳解

    Vue混合文件使用以及ref的引用實(shí)例詳解

    ref用來(lái)輔助開(kāi)發(fā)者在不依賴于jQuery的情況下,獲取DOM元素或組件的引用,下面這篇文章主要給大家介紹了關(guān)于Vue混合文件使用以及ref的引用的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Vue按照順序?qū)崿F(xiàn)多級(jí)彈窗效果 附Demo

    Vue按照順序?qū)崿F(xiàn)多級(jí)彈窗效果 附Demo

    這篇文章主要介紹了Vue按照順序?qū)崿F(xiàn)多級(jí)彈窗效果 附Demo,通過(guò)實(shí)例代碼介紹了單個(gè)彈窗和多級(jí)彈窗的實(shí)現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • VUE常見(jiàn)知識(shí)疑點(diǎn)問(wèn)題總結(jié)

    VUE常見(jiàn)知識(shí)疑點(diǎn)問(wèn)題總結(jié)

    這篇文章主要介紹了VUE常見(jiàn)知識(shí)疑點(diǎn)問(wèn)題總結(jié),詳細(xì)介紹了vue.js @click和v-on:click有什么區(qū)別,v-on和v-bind的區(qū)別,通過(guò)本文可以認(rèn)識(shí)Vue的export、export default、import的詳細(xì)介紹,感興趣的朋友一起看看吧
    2024-02-02
  • vueCli4如何配置vue.config.js文件

    vueCli4如何配置vue.config.js文件

    這篇文章主要介紹了vueCli4如何配置vue.config.js文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Vue使用element-ui實(shí)現(xiàn)菜單導(dǎo)航

    Vue使用element-ui實(shí)現(xiàn)菜單導(dǎo)航

    這篇文章主要為大家詳細(xì)介紹了Vue使用element-ui實(shí)現(xiàn)菜單導(dǎo)航,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • vue實(shí)現(xiàn)下拉菜單效果

    vue實(shí)現(xiàn)下拉菜單效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)下拉菜單效果,運(yùn)用了hover顯示與隱藏以及定位,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • 深入淺析vue中cross-env的使用

    深入淺析vue中cross-env的使用

    cross-env是跨平臺(tái)設(shè)置和使用環(huán)境變量的腳本。這篇文章主要介紹了vue中cross-env的使用,需要的朋友可以參考下
    2019-09-09
  • vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例

    vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例

    今天小編就為大家分享一篇vue 實(shí)現(xiàn)在函數(shù)中觸發(fā)路由跳轉(zhuǎn)的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09

最新評(píng)論