Vue 3的watchEffect、watch、computed API詳解

watchEffect

執(zhí)行監(jiān)聽

watchEffect比較奇特,它跟Vue 2的watch有所區(qū)別,它的寫法是:

watchEffect(() => {
  // 執(zhí)行一些操作,其中必須含有響應(yīng)式變量
})

為什么感覺怪怪的?watchEffect并沒有要求你聲明被監(jiān)聽的變量,而是,你在執(zhí)行體里寫哪個(gè)變量,Vue就收集、監(jiān)聽哪個(gè)變量,而且可以同時(shí)監(jiān)聽多個(gè)變量,看下例:

<template>
  <div>
    <button @click="r++">{{ r }}</button>
    <button @click="s.a++">{{ s.a }}</button>
    <button @click="s.b++">{{ s.b }}</button>
    <button @click="s.a++;s.b++">{{ s.a }} - {{ s.b }}</button>
  </div>
</template>

<script>
import { ref, computed, watchEffect } from 'vue';
export default {
  setup() {
    let r = ref(10);
    watchEffect(() => {
      console.log(r.value);
    });
    let s = ref({a: 100, b: 200});
    watchEffect(() => {
      console.log('a:', s.value.a);
    });
    watchEffect(() => {
      console.log('b:', s.value.b);
    });
    watchEffect(() => {
      console.log('a - b:', s.value.a + '-' + s.value.b);
    });
    watchEffect(() => {
      console.log('value:', s.value);
    });
    return {
      r,s
    };
  },
};
</script>

可以看到:

  1. 首先,watchEffect是立即執(zhí)行的,所以組件初始化的時(shí)候就全部執(zhí)行了一遍。

  2. 點(diǎn)擊button1,打印10,很好理解。

  3. s的傳入值是個(gè)對(duì)象,button2修改的是屬性a,那么,只有監(jiān)聽屬性a的監(jiān)聽器才會(huì)有反應(yīng),只跟屬性b相關(guān)的監(jiān)聽是不會(huì)有反應(yīng)的,只監(jiān)聽s.value的監(jiān)聽器也不會(huì)有反應(yīng)。點(diǎn)擊button3和button4也會(huì)印證這個(gè)結(jié)論。

  4. 在watchEffect里操作響應(yīng)式數(shù)據(jù),不會(huì)引起無限循環(huán)監(jiān)聽,這雖然很顯而易見,但是也在此說一句。

  5. 多個(gè)watchEffect的執(zhí)行順序是watchEffect的書寫順序。

  6. watchEffect拿不到更新前的值,這一點(diǎn)要注意。

停止監(jiān)聽

  1. 自動(dòng)停止

先說watchEffect生命周期的開始,是從組件的setup()函數(shù)或生命周期鉤子被調(diào)用時(shí)開始。自動(dòng)停止是在組件卸載時(shí)自動(dòng)停止。

  1. 手動(dòng)停止

將watchEffect賦值給變量,執(zhí)行這個(gè)變量即可手動(dòng)停止。比如:

const xx = watchEffect(() => {
  console.log('a:', s.value.a);
  s.value.a += 10
});
// 后來某個(gè)時(shí)間執(zhí)行了:
xx(); // 停止監(jiān)聽

清除副作用

官方文檔:https://v3.cn.vuejs.org/guide/reactivity-computed-watchers.html#清除副作用

官方文檔里偶爾會(huì)蹦出來一個(gè)詞“副作用”,初學(xué)者看完一頭霧水,什么鬼副作用?英文文檔里副作用是Side Effect,到底什么意思?

純函數(shù)里的副作用概念

副作用其實(shí)是一個(gè)比較生僻的概念,最早來自于“純函數(shù)”,純函數(shù)是編程界早期的一個(gè)概念,具體可以看(https://zhuanlan.zhihu.com/p/139659155https://juejin.cn/post/6950059795659358221)。純函數(shù)特征:

它應(yīng)始終返回相同的值。不管調(diào)用該函數(shù)多少次,無論今天、明天還是將來某個(gè)時(shí)候調(diào)用它。
自包含(不使用全局變量)。
它不應(yīng)修改程序的狀態(tài)或引起副作用(修改全局變量)。

注意看,這里就出現(xiàn)了“副作用”。所以,純函數(shù)的副作用就是:

一個(gè)函數(shù)除了返回確定的值之外,還做了其他的事情,那么這個(gè)函數(shù)做的這些事情就叫做“副作用”。

比如console.log(123)就有副作用,它返回undefined是主作用,但是我們不需要它的主作用,它的副作用就是在控制臺(tái)打印123,我們要的是它的副作用。

再比如:

let counter = 0;

// 有副作用,副作用是把外部變量改了
incCounter() {
    counter += 1;
    return counter;
}

// 沒有副作用
incNumber(m) {
    return m + 1;
}
React中的副作用概念

React等框架早先就在使用這個(gè)詞,Vue從3.0開始,在文檔里出現(xiàn)這個(gè)詞。

那么,在React中,是不是副作用也是這個(gè)定義呢?未必??纯碦eact是怎么說的:

你之前可能已經(jīng)在 React 組件中執(zhí)行過數(shù)據(jù)獲取、訂閱或者手動(dòng)修改過 DOM。我們統(tǒng)一把這些操作稱為“副作用”,或者簡(jiǎn)稱為“作用”。
useEffect 就是一個(gè) Effect Hook,給函數(shù)組件增加了操作副作用的能力。它跟 class 組件中的 componentDidMount、componentDidUpdatecomponentWillUnmount 具有相同的用途,只不過被合并成了一個(gè) API。
例如,下面這個(gè)組件在 React 更新 DOM 后會(huì)設(shè)置一個(gè)頁(yè)面標(biāo)題:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 相當(dāng)于 componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    // 使用瀏覽器的 API 更新頁(yè)面標(biāo)題
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

當(dāng)你調(diào)用 useEffect 時(shí),就是在告訴 React 在完成對(duì) DOM 的更改后運(yùn)行你的“副作用”函數(shù)。由于副作用函數(shù)是在組件內(nèi)聲明的,所以它們可以訪問到組件的 props 和 state。默認(rèn)情況下,React 會(huì)在每次渲染后調(diào)用副作用函數(shù) —— 包括第一次渲染的時(shí)候。
副作用函數(shù)還可以通過返回一個(gè)函數(shù)來指定如何“清除”副作用。

看完了之后,我們就知道React對(duì)于副作用的解釋:

在執(zhí)行所有業(yè)務(wù)邏輯之前,也就是組件初始化的時(shí)候,組件根據(jù)開發(fā)者的設(shè)定,由自身驅(qū)動(dòng)的第一次DOM修改,就是主作用。此時(shí),組件還沒有執(zhí)行任何一行邏輯代碼。

主作用之后,組件就開始執(zhí)行用戶邏輯了,這里你眼里的業(yè)務(wù)邏輯代碼,在React眼里都是副作用。

Vue 3里的副作用概念

跟React應(yīng)該是說的一個(gè)意思,具體說:

所以首先了解一下“主作用”,在Vue世界里,視圖層和DOM層是兩碼事,盡管一些初級(jí)程序員認(rèn)為它們是一碼事。變更響應(yīng)式數(shù)據(jù)的主作用就是變更后的數(shù)據(jù)能渲染到視圖層。前端還有比這個(gè)事更重要的事嗎?沒有吧。

副作用就是響應(yīng)式數(shù)據(jù)的變更造成的其他連鎖反應(yīng),以及后續(xù)邏輯,這些連鎖反應(yīng)都叫副作用。在藥物學(xué)里,副作用往往是不良反應(yīng),但是在Vue 3里并不是。上面標(biāo)題里說“清除副作用”,也并不是說因?yàn)楦弊饔檬遣涣挤磻?yīng)所以要清除,而是Vue 3提供一個(gè)方法讓你隨時(shí)可以取消副作用。

副作用主要有:

  1. DOM更新

  2. watchEffect

  3. watch

  4. computed

  5. ...

你沒看錯(cuò),既然更新視圖層才是主作用,那么視圖層更新到DOM上在Vue眼里是副作用,而且,變更響應(yīng)式數(shù)據(jù)觸發(fā)執(zhí)行computed和觸發(fā)執(zhí)行watchEffect當(dāng)然也是副作用。所以watchEffect本身就是副作用。

清除副作用是什么意思

那么官方文檔說的“清除副作用”到底在說什么?它意思是說,如果有些副作用是異步的,這就意味著你可以取消它,那么Vue創(chuàng)始人就給你提供了一個(gè)方法,讓你優(yōu)雅的取消這些異步副作用。

比如你有一個(gè)頁(yè)碼組件,里面有5個(gè)頁(yè)碼,點(diǎn)擊就會(huì)異步請(qǐng)求數(shù)據(jù)。于是我就做了一個(gè)監(jiān)聽,監(jiān)聽當(dāng)前頁(yè)碼,只要有變化就ajax一次。下例是不可直接運(yùn)行的演示代碼:

    let content = '';
    const pageNumber = ref(1);
    function onClickPageNumber(val) {
      pageNumber.value = val;
    }
    watchEffect(() => {
      ajax({pageNumber}).then(response => {
        content = response.data;
      })
    });

現(xiàn)在問題是,如果我點(diǎn)擊的比較快,從1到5全點(diǎn)了一遍,那么會(huì)有5個(gè)ajax請(qǐng)求,最終頁(yè)面會(huì)顯示第幾頁(yè)的內(nèi)容?你說第5頁(yè)?那你是假定請(qǐng)求第5頁(yè)的ajax響應(yīng)的最晚,事實(shí)呢?并不一定。于是這就會(huì)導(dǎo)致錯(cuò)亂。還有一個(gè)問題,我連續(xù)快速點(diǎn)5次頁(yè)碼,等于我并不想看前4頁(yè)的內(nèi)容,那么是不是前4次的請(qǐng)求都屬于帶寬浪費(fèi)?這也不好。于是官方就給出了一種解決辦法:

首先,你的異步操作必須是能中止的異步操作,對(duì)于定時(shí)器來講中止定時(shí)器很容易,clearInterval之類的就可以,但對(duì)于ajax來講,需要借助ajax庫(kù)(比如axios)提供的中止ajax辦法來中止ajax。現(xiàn)在我寫一個(gè)能直接運(yùn)行的范例演示一下中止異步操作:

我先搭建一個(gè)最簡(jiǎn)Node服務(wù)器,3300端口的:

    const http = require('http');
    const server = http.createServer((req, res) => {
      res.setHeader('Access-Control-Allow-Origin', "*");
      res.setHeader('Access-Control-Allow-Credentials', true);
      res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
    });
    server.listen(3300, () => {
      console.log('Server is running...');
    });
    server.on('request', (req, res) => {
      setTimeout(() => {
        if (/\d.json/.test(req.url)) {
          const data = {
            content: '我是內(nèi)容,來自' + req.url
          }
          res.end(JSON.stringify(data));
        }
      }, Math.random() * 2000);
    });

清除副作用的核心有2點(diǎn):

  1. 異步副作用要給出取消自身的辦法

  2. watchEffect提供取消副作用的接口,也就是onInvalidate方法。Invalidate中文譯義是作廢,onInvalidate也就是作廢監(jiān)聽器。

<template>
  <div>
    <div>content: {{ content }}</div>
    <button @click="pageNumber = (pageNumber++ % 5) + 1">{{ pageNumber }}</button>
  </div>
</template>

<script>
import axios from 'axios';
import { ref, watchEffect } from 'vue';
export default {
  setup() {
    let pageNumber = ref(1);
    let content = ref('');

    watchEffect((onInvalidate) => {
      // const CancelToken = axios.CancelToken;
      // const source = CancelToken.source();
      // onInvalidate(() => {
      //   source.cancel();
      // });
      axios
        .get(`http://localhost:3300/${pageNumber.value}.json`, {
          // cancelToken: source.token,
        })
        .then((response) => {
          content.value = response.data.content;
        })
        .catch(function (err) {
          if (axios.isCancel(err)) {
            console.log('Request canceled', err.message);
          }
        });
    });
    return {
      pageNumber,
      content,
    };
  },
};
</script>

上面注釋掉的代碼先保持注釋掉,然后我們經(jīng)過20多次瘋狂點(diǎn)擊之后,得到這個(gè)結(jié)果,顯然,內(nèi)容錯(cuò)亂了:

image.png

現(xiàn)在我取消注釋,重新20多次瘋狂點(diǎn)擊,得到的結(jié)果就正確了:

image.png

除了最后一個(gè)請(qǐng)求,上面那些請(qǐng)求有2種結(jié)局:

  1. 一種是響應(yīng)的太快,來不及取消的請(qǐng)求,這種請(qǐng)求會(huì)返回200,不過既然它響應(yīng)太快,沒有任何一次后續(xù)ajax能夠來得及取消它,說明任何一次后續(xù)ajax開始之前,它就已經(jīng)結(jié)束了,那么它一定會(huì)被后續(xù)某些請(qǐng)求所覆蓋,所以這類請(qǐng)求的content會(huì)顯示一瞬間,然后被后續(xù)的請(qǐng)求覆蓋,絕對(duì)不會(huì)比后面的請(qǐng)求還晚。

  2. 另一種就是紅色的那些被取消的請(qǐng)求,因?yàn)轫憫?yīng)的慢,所以被取消掉了。

所以最終結(jié)果一定是正確的,而且節(jié)省了很多帶寬,也節(jié)省了系統(tǒng)開銷。

這就是官方說的“清除副作用”。清除定時(shí)器更簡(jiǎn)單,我不舉例了。

副作用刷新時(shí)機(jī)

官方文檔:https://v3.cn.vuejs.org/guide/reactivity-computed-watchers.html#副作用刷新時(shí)機(jī)

官方文檔里的“副作用刷新時(shí)機(jī)”更晦澀,我解釋一下。

Vue 的響應(yīng)性系統(tǒng)會(huì)緩存副作用函數(shù),并異步地刷新它們,這樣可以避免同一個(gè)“tick”中多個(gè)狀態(tài)改變導(dǎo)致的不必要的重復(fù)調(diào)用。

同一個(gè)“tick”的意思是,Vue的內(nèi)部機(jī)制會(huì)以最科學(xué)的計(jì)算規(guī)則將視圖刷新請(qǐng)求合并成一個(gè)一個(gè)的"tick",每個(gè)“tick”刷新一次視圖,比如a=1;b=2;只會(huì)觸發(fā)一次視圖刷新。$nextTick的Tick就是指這個(gè)。

繼續(xù)說,比如有個(gè)watchEffect監(jiān)聽了2個(gè)變量a和b,我的業(yè)務(wù)寫了a=1;b=2;,你覺得監(jiān)聽器會(huì)調(diào)用2次?當(dāng)然不會(huì),Vue會(huì)合并成1次去執(zhí)行,代碼如下,console.log只會(huì)執(zhí)行一次:

<template>
  <div>
    <button
      @click="
        r++;
        s++;
      "
    >
      {{ r }} - {{ s }}
    </button>
  </div>
</template>

<script>
import { ref, watchEffect } from 'vue';
export default {
  setup() {
    let r = ref(2);
    let s = ref(10);
    watchEffect(() => {
      console.log(r.value, s.value);
    });
    return {
      r,
      s,
    };
  },
};
</script>

在核心的具體實(shí)現(xiàn)中,組件的update函數(shù)也是一個(gè)被偵聽的副作用。當(dāng)一個(gè)用戶定義的副作用函數(shù)進(jìn)入隊(duì)列時(shí),默認(rèn)情況下,會(huì)在所有的組件update前執(zhí)行。

所謂組件的update函數(shù)是Vue內(nèi)置的用來更新DOM的函數(shù),它也是副作用,上文已經(jīng)提到過。這時(shí)候有一個(gè)問題,就是默認(rèn)下,Vue會(huì)先執(zhí)行組件DOM update,還是先執(zhí)行監(jiān)聽器?測(cè)一下:

<template>
  <div>
    <button
      id="aa"
      @click="
        r++;
        s++;
      "
    >
      {{ r }} - {{ s }}
    </button>
  </div>
</template>

<script>
import { ref, watchEffect } from 'vue';
export default {
  setup() {
    let r = ref(2);
    let s = ref(10);
    watchEffect(
      () => {
        console.log(r.value, s.value);
        console.log(document.querySelector('#aa') && document.querySelector('#aa').innerText);
      }
    );
    return {
      r,
      s,
    };
  },
};
</script>

點(diǎn)擊若干次(比如2次)按鈕,得到的結(jié)果是:

image.png
image.png

為什么點(diǎn)之前按鈕的innerText打印null?因?yàn)槭聦?shí)就是默認(rèn)先執(zhí)行監(jiān)聽器,然后更新DOM,此時(shí)DOM還未生成,當(dāng)然是null。

當(dāng)我第1和2次點(diǎn)擊完,你會(huì)發(fā)現(xiàn),document.querySelector('#aa').innerText獲取到的總是點(diǎn)擊之前DOM的內(nèi)容。這也說明,默認(rèn)Vue先執(zhí)行監(jiān)聽器,所以取到了上一次的內(nèi)容,然后執(zhí)行組件update。

Vue 2其實(shí)也是這種機(jī)制,Vue 2使用this.$nextTick()去獲取組件更新完成之后的DOM,在watchEffect里就不需要用this.$nextTick()(也沒法用),有一個(gè)辦法能獲取組件更新完成之后的DOM,就是使用:

watchEffect(
  () => {
    /* ... */
  },
  {
    flush: 'post'
  }
)

現(xiàn)在設(shè)上flush配置項(xiàng),重新進(jìn)入組件,再看看:

沒設(shè)flush: 'post' 設(shè)了flush: 'post'
image.png
image.png

所以結(jié)論是,如果要操作“更新之后的DOM”,就要配置flush: 'post'。

watch

Vue 3 watch與Vue 2 watch對(duì)比

  1. Vue 3 watch與Vue 2的實(shí)例方法vm.$watch(也就是this.$watch)的基本用法差不多,只不過程序員大多使用watch配置項(xiàng),可能對(duì)$watch實(shí)例方法不太熟。實(shí)例方法的一個(gè)優(yōu)勢(shì)是更靈活,第一個(gè)參數(shù)可以接受一個(gè)函數(shù),等于是接受了一個(gè)getter函數(shù)。
<template>
  <div>
    <button @click="r++">{{ r }}</button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';
export default {
  setup() {
    let r = ref(1);
    let s = ref(10);
    watch(
      () => r.value + s.value,
      (newVal, oldVal) => {
        console.log(newVal, oldVal);
      }
    );
    return {
      r,
      s,
    };
  },
};
</script>
  1. Vue 3 watch增加了同時(shí)監(jiān)聽多個(gè)變量的能力,用數(shù)組表達(dá)要監(jiān)聽的變量。回調(diào)參數(shù)是這種結(jié)構(gòu):[newR, newS, newT], [oldR, oldS, oldT],不要理解成其他錯(cuò)誤的結(jié)構(gòu)。
<template>
  <div>
    <button @click="r++">{{ r }}</button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';
export default {
  setup() {
    let r = ref(1);
    let s = ref(10);
    let t = ref(100);
    watch(
      [r, s, t],
      ([newR, newS, newT], [oldR, oldS, oldT]) => {
        console.log([newR, newS, newT], [oldR, oldS, oldT]);
      }
    );
    return {
      r,
    };
  },
};
</script>
  1. 被監(jiān)聽的變量必須是:A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.也就是說,可以是getter/effect函數(shù)、ref、Proxy以及它們的數(shù)組。絕對(duì)不可以是純對(duì)象或基本數(shù)據(jù)。

  2. 想要Vue 3的watch立即執(zhí)行,可以在watch的最后一個(gè)參數(shù)寫上{immediate: true}。

  3. Vue 3的深度監(jiān)聽還有沒有?當(dāng)然有,而且默認(rèn)就是,無需聲明。當(dāng)然,前提是深層property也是響應(yīng)式的。如果深層property無響應(yīng)式,那么即便寫上{deep: true}也沒用。

Vue 3 watch與Vue 3 watchEffect的差異

這方面官方文檔說的還可以:

  • 惰性地執(zhí)行副作用,也就是說不會(huì)立即執(zhí)行一次;
  • 更具體地說明應(yīng)觸發(fā)偵聽器重新運(yùn)行的狀態(tài),這句話翻譯還是很晦澀,其實(shí)意思是說,你現(xiàn)在能一眼看出來哪個(gè)變量被監(jiān)聽;
  • 能訪問偵聽狀態(tài)的先前值和當(dāng)前值,不要小看這個(gè)差別,有時(shí)候拿不到先前值就沒法進(jìn)行業(yè)務(wù)。

所以,當(dāng)你不希望立即執(zhí)行一次監(jiān)聽器,或者需要拿到先前值,或者想明確表明哪些變量被監(jiān)聽了,就用watch。

其他差異有:

  1. 如果監(jiān)聽一個(gè)Proxy變量p,它的內(nèi)部值結(jié)構(gòu)是{a: {b: {c: 2}}}{a: {b: {c: {d: 3}}}},我打算監(jiān)聽p.a.b.c,那么:
watchEffect watch且p.a.b.c是基本類型 watch且p.a.b.c是引用類型
必須監(jiān)聽p.a.b.c自身 必須監(jiān)聽p.a.b.c的任意一級(jí)上級(jí)property 監(jiān)聽p.a.b.c自身和任意上級(jí)property均可
  1. 如果監(jiān)聽ref,跟上面類似,只是有2個(gè)注意事項(xiàng):一是p后面不要忘記加.value,二是所謂“p.value.a.b.c的任意上級(jí)property”最高只允許到p.value,不能到p。

Vue 3 watch與Vue 3 watchEffect的共性

官方說,watch也有停止偵聽,清除副作用、副作用刷新時(shí)機(jī)和偵聽器調(diào)試行為。簡(jiǎn)單舉例:

  1. watch停止監(jiān)聽:

停止監(jiān)聽watch很簡(jiǎn)單,watch的時(shí)候就必須賦值給一個(gè)變量,這時(shí)候就開始監(jiān)聽。想停止監(jiān)聽就把這個(gè)變量當(dāng)函數(shù)執(zhí)行一下。

<template>
  <div>
    <button @click="r++">{{ r }}</button>
    <button @click="s()">stop</button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';
export default {
  setup() {
    let r = ref(2);
    let s = watch(r, () => {
      console.log(r.value);
    });
    return {
      r,
      s,
    };
  },
};
</script>
  1. watch清除副作用:
<template>
  <div>
    <div>content: {{ content }}</div>
    <button @click="pageNumber = (pageNumber++ % 5) + 1">{{ pageNumber }}</button>
  </div>
</template>

<script>
import axios from 'axios';
import { ref, watch } from 'vue';
export default {
  setup() {
    let pageNumber = ref(1);
    let content = ref('');

    watch(pageNumber, (newVal, oldVal, onInvalidate) => {
      const CancelToken = axios.CancelToken;
      const source = CancelToken.source();
      onInvalidate(() => {
        source.cancel();
      });
      axios
        .get(`http://localhost:3300/${pageNumber.value}.json`, {
          cancelToken: source.token,
        })
        .then((response) => {
          content.value = response.data.content;
        })
        .catch(function (err) {
          if (axios.isCancel(err)) {
            console.log('Request canceled', err.message);
          }
        });
    });
    return {
      pageNumber,
      content,
    };
  },
};
</script>
  1. 調(diào)整副作用刷新時(shí)機(jī),可以嘗試注釋flush: 'post',作為對(duì)比:
<template>
  <div>
    <button
      id="aa"
      @click="
        r++;
        s++;
      "
    >
      {{ r }} - {{ s }}
    </button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';
export default {
  setup() {
    let r = ref(2);
    let s = ref(10);
    watch(r,
      () => {
        console.log(r.value, s.value);
        console.log(document.querySelector('#aa') && document.querySelector('#aa').innerText);
      },
      {
        flush: 'post'
      }
    );
    return {
      r,
      s,
    };
  },
};
</script>

computed

Vue 3跟Vue 2的computed的差別在于,Vue 2是所有計(jì)算屬性都是根對(duì)象的屬性,Vue 3是計(jì)算屬性都是獨(dú)立變量,其他區(qū)別很小,就不細(xì)說了。

Vue 3 computed特點(diǎn):

  • computed默認(rèn)接收getter函數(shù),也可以接收一個(gè)對(duì)象,對(duì)象里有g(shù)et和set方法。set方法接收一個(gè)val參數(shù)。初學(xué)者可能會(huì)忘記寫getter函數(shù),只寫計(jì)算表達(dá)式,要注意這點(diǎn)。

  • computed一定返回ref對(duì)象,所以并不需要在計(jì)算函數(shù)里給返回值添加響應(yīng)式,這屬于畫蛇添足。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容