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
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ě:
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>
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ě
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
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:
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.firstName
a this.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:
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:
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.