最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2022年10月21日 15:09:52   作者:Young soul2  
computed又被稱作計(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使用Vue.extend創(chuàng)建全局toast組件實(shí)例

    vue使用Vue.extend創(chuàng)建全局toast組件實(shí)例

    這篇文章主要介紹了vue使用Vue.extend創(chuàng)建全局toast組件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Vue.js原理分析之nextTick實(shí)現(xiàn)詳解

    Vue.js原理分析之nextTick實(shí)現(xiàn)詳解

    這篇文章主要給大家介紹了關(guān)于Vue.js原理分析之nextTick實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Vue3中插槽(slot)用法匯總(推薦)

    Vue3中插槽(slot)用法匯總(推薦)

    這篇文章主要介紹了Vue3中插槽(slot)用法匯總,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • 解決vue-cli輸入命令vue ui沒(méi)效果的問(wèn)題

    解決vue-cli輸入命令vue ui沒(méi)效果的問(wèn)題

    這篇文章主要介紹了解決vue-cli輸入命令vue ui沒(méi)效果的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • vue3引入highlight.js進(jìn)行代碼高亮的方法實(shí)例

    vue3引入highlight.js進(jìn)行代碼高亮的方法實(shí)例

    最近忙著開發(fā)自己的開發(fā)腳手架,在做代碼生成器的時(shí)候,有個(gè)預(yù)覽功能,需要讓代碼高亮,下面這篇文章主要給大家介紹了關(guān)于vue3引入highlight.js進(jìn)行代碼高亮的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • Vue3中Cesium地圖初始化及地圖控件配置方法

    Vue3中Cesium地圖初始化及地圖控件配置方法

    本文中,我們主要介紹Cesium在Vue3運(yùn)行環(huán)境的配置,及Cesium實(shí)例中控件的顯隱設(shè)置,本項(xiàng)目基于pnpm安裝,也可使用其他包管理器進(jìn)行安裝,如npm或yarn,本文通過(guò)示例代碼對(duì)vue初始化Cesium地圖相關(guān)知識(shí)介紹的非常詳細(xì),需要的朋友參考下吧
    2023-07-07
  • 一定要知道的 25 個(gè) Vue 技巧

    一定要知道的 25 個(gè) Vue 技巧

    這篇文章主要給大家分享將 prop 限制為類型列表、默認(rèn)內(nèi)容和擴(kuò)展點(diǎn)、使用引號(hào)觀察嵌套值、知道何時(shí)使用 v-if、單作用域 slot 的簡(jiǎn)寫、有條件地渲染slot等25 個(gè)Vue 技巧,下文是下相關(guān)資料,需要的朋友可以參考一下
    2021-11-11
  • 更強(qiáng)大的vue ssr實(shí)現(xiàn)預(yù)取數(shù)據(jù)的方式

    更強(qiáng)大的vue ssr實(shí)現(xiàn)預(yù)取數(shù)據(jù)的方式

    這篇文章主要介紹了更強(qiáng)大的 vue ssr 預(yù)取數(shù)據(jù)的方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Vue實(shí)現(xiàn)上下層疊輪播圖

    Vue實(shí)現(xiàn)上下層疊輪播圖

    這篇文章主要介紹了Vue實(shí)現(xiàn)上下層疊輪播圖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 解讀Vue-loader的相關(guān)知識(shí)

    解讀Vue-loader的相關(guān)知識(shí)

    這篇文章主要介紹了解讀Vue-loader的相關(guān)知識(shí),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03

最新評(píng)論

洪雅县| 阜宁县| 阿勒泰市| 安多县| 通化市| 樟树市| 固镇县| 安溪县| 田林县| 应用必备| 无棣县| 丹江口市| 宁阳县| 山阳县| 大埔区| 年辖:市辖区| 高碑店市| 峨边| 澄迈县| 水富县| 芦溪县| 亳州市| 中超| 祁连县| 大悟县| 保定市| 威海市| 安多县| 循化| 秦安县| 交口县| 庆云县| 宁南县| 德庆县| 昌宁县| 扎兰屯市| 利津县| 闽清县| 遂溪县| 凭祥市| 濮阳县|