Computed

Computed 介紹

Computed 是

computed 是計算屬性:值是根據其他資料衍生出來的結果

Vue 的 computed 計算屬性會自動追蹤響應式依賴,它就像是一個加上「感應器」的 getter,Vue 會追蹤裡面用到的 reactive 變數。當使用 computed 時,一旦那些依賴的變數變動,這個計算結果就會重新運算。

和 method 不同,computed 會快取結果:只要依賴沒變,就會直接回傳上次的值,不會重新執行函式,這在需要計算成本的情境(例如複雜排序、過濾等)尤其重要。

這種「根據其他資料計算出衍生資料,且具備快取與響應能力」的設計,就是 computed 的本質。

把 computed 想像成「智慧型小白板」,它會寫下根據資料算出的東西,只要來源資料沒動,它就不會擦掉重寫,節省計算資源。

computed()​

  1. 具備「快取」的 effect
  2. 只要依賴的值不變時就不會重新計算
  3. 實作上會建立 lazy 的 effect,並追蹤依賴是否髒了(dirty)
  4. 支援 setter(雙向綁定)
computed
Preview
<script setup>
import { ref, computed } from 'vue'

const height = ref(180)
const weight = ref(75)

const bmi = computed(() => {
  const h = height.value / 100
  return (weight.value / (h * h)).toFixed(1)
})

const suggestion = computed(() => {
  const value = parseFloat(bmi.value)
  if (value < 18.5) return '體重過輕'
  if (value < 24) return '正常'
  if (value < 27) return '過重'
  return '肥胖'
})

const suggestionClass = computed(() => {
  switch (suggestion.value) {
    case '體重過輕':
      return 'text-blue-500'
    case '正常':
      return 'text-green-600'
    case '過重':
      return 'text-yellow-500'
    case '肥胖':
      return 'text-red-600'
    default:
      return ''
  }
})
</script>

<template>
  <div class="p-4">
    <label>
      身高(cm):
      <input type="number" v-model.number="height" />
    </label>
    <br />
    <label>
      體重(kg):
      <input type="number" v-model.number="weight" />
    </label>
    <br />
    <p>BMI:<strong>{{ bmi }}</strong></p>
    <p>建議:<span :class="suggestionClass">{{ suggestion }}</span></p>
  </div>
</template>

避免 mutate 操作

計算屬性的 getter 應只做計算而沒有任何其他的副作用(不要改變其他狀態、在 getter 中做非同步請求或者更改 DOM),getter 的職責應該僅為計算和返回該值。而 computed 的返回值應該被視為只讀的,並且永遠不應該被更改——應該更新它所依賴的源狀態以觸發新的計算。

reverse()sort() 這類方法是「會改變原陣列本身」的,所以在 computed 裡面使用時,如果不小心直接操作原始資料,就等於破壞了響應式狀態的「單向資料流」。

bad
good
<script setup>
import { computed } from 'vue'
const sortedNumbers = computed(() => numbers.value.sort())
const reversed = computed(() => numbers.value.reverse())
</script>