Skip to content

Computed proměnné

Jednoduchý příklad

Výrazy přímo v šabloně jsou velmi pohodlné, ale jsou určeny pouze pro jednoduché operace. Příliš mnoho logiky vložené do vašich šablon může způsobit, že budou přeplácané a obtížně udržovatelné. Například, pokud máme objekt s vnořeným polem:

js
export default {
  data() {
    return {
      author: {
        name: 'Jan Novák',
        books: [
          'Vue 2 - Pokročilý průvodce',
          'Vue 3 - Základní průvodce',
          'Vue 4 - Tajemství'
        ]
      }
    }
  }
}
js
const author = reactive({
  name: 'Jan Novák',
  books: [
    'Vue 2 - Pokročilý průvodce',
    'Vue 3 - Základní průvodce',
    'Vue 4 - Tajemství'
  ]
})

A chceme zobrazovat různé texty v závislosti na tom, zda author napsal nebo nenapsal nějaké knihy:

template
<p>Již publikoval knihu:</p>
<span>{{ author.books.length > 0 ? 'Ano' : 'Ne' }}</span>

V tuto chvíli začíná být šablona trochu nepřehledná. Musíme se na ni chvíli dívat, než si uvědomíme, že provádí výpočet v závislosti na author.books. Ještě důležitější je, že se asi nechceme opakovat, pokud tento výpočet potřebujeme zahrnout do šablony vícekrát.

Proto se pro komplexní logiku, která zahrnuje reaktivní data, doporučuje použít computed proměnnou. Zde je stejný příklad po úpravě:

js
export default {
  data() {
    return {
      author: {
        name: 'Jan Novák',
        books: [
          'Vue 2 - Pokročilý průvodce',
          'Vue 3 - Základní průvodce',
          'Vue 4 - Tajemství'
        ]
      }
    }
  },
  computed: {
    // getter computed proměnné
    publishedBooksMessage() {
      // `this` odkazuje na instanci komponenty
      return this.author.books.length > 0 ? 'Ano' : 'Ne'
    }
  }
}
template
<p>Již publikoval knihu:</p>
<span>{{ publishedBooksMessage }}</span>

Vyzkoušejte si to

Deklarovali jsme computed proměnnou publishedBooksMessage.

Zkuste změnit hodnotu pole books v návratové hodnotě možnosti data a uvidíte, jak se publishedBooksMessage odpovídajícím způsobem změní.

Binding computed proměnných v šablonách můžete provést stejně jako u normálních proměnných. Vue si uvědomuje, že this.publishedBooksMessage závisí na hodnotě this.author.books, takže když se this.author.books změní, aktualizuje všude všechny reference na this.publishedBooksMessage.

Viz také: Typování computed proměnných

vue
<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'Jan Novák',
  books: [
    'Vue 2 - Pokročilý průvodce',
    'Vue 3 - Základní průvodce',
    'Vue 4 - Tajemství'
  ]
})

// computed ref objekt
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Ano' : 'Ne'
})
</script>

<template>
  <p>Již publikoval knihu:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

Vyzkoušejte si to

Zde jsme deklarovali computed proměnnou publishedBooksMessage. Funkce computed() očekává, že jí bude předána getter funkce, a vrácená hodnota je computed ref. Podobně jako u normálních refs můžete k vypočítanému výsledku přistupovat přes výraz publishedBooksMessage.value. Computed refs jsou v šablonách také automaticky rozbaleny, takže na ně můžete ve výrazech v šablonách odkazovat bez .value.

Computed proměnná automaticky sleduje své reaktivní závislosti. Vue si uvědomuje, že publishedBooksMessage závisí na author.books, takže když se author.books změní, aktualizuje všude všechny reference na publishedBooksMessage.

Viz také: Typování computed proměnných

Computed caching vs. funkce

Možná jste si všimli, že stejného výsledku můžeme dosáhnout zavoláním funkce ve výrazu šablony:

template
<p>{{ calculateBooksMessage() }}</p>
js
// v komponentě
methods: {
  calculateBooksMessage() {
    return this.author.books.length > 0 ? 'Ano' : 'Ne'
  }
}
js
// v komponentě
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Ano' : 'Ne'
}

Místo computed proměnné můžeme stejnou logiku definovat jako funkci. Pro konečný výsledek jsou oba přístupy skutečně naprosto stejné. Rozdíl je však v tom, že computed proměnné se na základě jejich reaktivních závislostí ukládají do mezipaměti. Computed proměnná se přehodnotí pouze tehdy, když se změní některé její reaktivní závislosti. To znamená, že pokud se author.books nezmění, vícenásobný přístup k hodnotě publishedBooksMessage okamžitě vrátí dříve vypočítaný výsledek, aniž by bylo nutné znovu spouštět getter funkci.

To také znamená, že následující computed proměnná se neaktualizuje nikdy, protože Date.now() není reaktivní závislost:

js
computed: {
  now() {
    return Date.now()
  }
}
js
const now = computed(() => Date.now())

Ve srovnání s tím vyvolání funkce ji spustí vždy, jakmile dojde k opětovnému vykreslení.

Proč caching potřebujeme? Představte si, že máme složitě získávanou proměnnou list, která vyžaduje procházení obrovského pole a spoustu výpočtů. Pak můžeme mít další computed proměnné, které na list závisí. Bez ukládání do mezipaměti bychom spouštěli getter pro list mnohem častěji, než je nutné! V případech, kdy caching nechcete, použijte místo toho volání funkce.

Computed proměnná, kterou lze měnit

Computed proměnné jsou ve výchozím nastavení pouze pro čtení. Pokud se pokusíte přiřadit do computed proměnné novou hodnotu, dostanete runtime warning. V těch vzácných případech, kdy „zapisovatelnou“ computed proměnnou potřebujete, ji můžete vytvořit tak, aby poskytovala getter i setter:

js
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      // getter
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
        // poznámka: zde používáme "destructuring assignment" syntaxi
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}

Když teď zadáte this.fullName = 'Jan Novák', zavolá se setter a this.firstNamethis.lastName budou odpovídajícím způsobem aktualizovány.

vue
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // poznámka: zde používáme "destructuring assignment" syntaxi
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

Když teď zadáte this.fullName = 'Jan Novák', zavolá se setter a this.firstNamethis.lastName budou odpovídajícím způsobem aktualizovány.

Získání předchozí hodnoty

  • Podporováno až od verze 3.4+

V případě, že to potřebujete, můžete získat předchozí hodnotu computed proměnné v podobě prvního parametru getter funkce:

js
export default {
  data() {
    return {
      count: 2
    }
  },
  computed: {
    // Tato computed proměnná vrátí hodnotu `count`, pokud je menší nebo rovno 3.
    // Když je `count` >=4, bude vrácena poslední hodnota splňující podmínku.
    alwaysSmall(previous) {
      if (this.count <= 3) {
        return this.count;
      }
      return previous;
    }
  }
}
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
// Tato computed proměnná vrátí hodnotu `count`, pokud je menší nebo rovno 3.
// Když je `count` >=4, bude vrácena poslední hodnota splňující podmínku.
const alwaysSmall = computed((previous) => {
  if (count.value <= 3) {
    return count.value;
  }
  return previous;
})
</script>

Pokud používáte zapisovatelnou computed proměnnou:

js
export default {
  data() {
    return {
      count: 2
    }
  },
  computed: {
    alwaysSmall: {
      get(previous) {
        if (this.count <= 3) {
          return this.count;
        }
        return previous;
      },
      set(newValue) {
        this.count = newValue * 2;
      }
    }
  }
}
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
const alwaysSmall = computed({
  get(previous) {
    if (count.value <= 3) {
      return count.value;
    }
    return previous;
  },
  set(newValue) {
    count.value = newValue * 2;
  }
})
</script>

Osvěčené postupy

Getter funkce by neměly mít vedlejší účinky

Je důležité si zapamatovat, že getter funkce pro computed proměnné by měly provádět pouze čisté výpočty a neměly by mít vedlejší účinky. Například uvnitř computed getter funkce neměňte jiný stav, nevytvářejte asynchronní volání, ani neměňte DOM! Představte si computed proměnnou jako deklarativní popis, jak odvodit hodnotu na základě jiných hodnot – její jedinou odpovědností by měl být výpočet a návrat této hodnoty. Později v příručce probereme, jak můžeme vedlejší účinky v reakci na změny stavu provádět pomocí watchers.

Vyhněte se změnám vypočítané hodnoty

Vrácená hodnota computed proměnné je odvozený stav. Berte to jako dočasný otisk (snapshot) – pokaždé, když se změní stav zdroje, vytvoří se nový snímek. Měnit obraz nedává smysl, takže vypočítaná návratová hodnota by měla být obsluhována jako read-only a nikdy by neměla být měněna – místo toho aktualizujte stav zdroje, na kterém závisí, což vyvolá nový výpočet.

Computed proměnné has loaded