Właściwości computed
Podstawowy przykład
Wyrażenia w szablonie są bardzo wygodne, ale są przeznaczone do prostych operacji. Umieszczanie zbyt dużej ilości logiki do szablonów może sprawić, że staną się one przeładowane i trudne do utrzymania. Na przykład, jeśli mamy obiekt z zagnieżdżoną tablicą:
js
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
I chcemy wyświetlić różne komunikaty w zależności od tego, czy author
ma już jakieś książki, czy nie:
template
<p>Czy wydał jakieś książki:</p>
<span>{{ author.books.length > 0 ? 'Tak' : 'Nie' }}</span>
W tym momencie szablon zaczyna być nieco przeładowany. Trzeba przez chwilę na niego popatrzeć, zanim zrozumiemy, że wykonuje on obliczenia w zależności od author.books
. Co ważniejsze, prawdopodobnie nie chcemy powtarzać tego samego obliczenia w kilku miejscach w szablonie.
Dlatego w przypadku bardziej złożonej logiki, która obejmuje dane reaktywne, zaleca się użycie właściwości computed. Oto ten sam przykład, ale zrefaktoryzowany:
vue
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// computed ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Tak' : 'Nie'
})
</script>
<template>
<p>Czy wydał jakieś książki:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Tutaj zadeklarowaliśmy właściwość computed publishedBooksMessage
. Funkcja computed()
oczekuje funkcji getter, a zwrócona wartość to computed ref. Podobnie jak normalne refy, możesz uzyskać dostęp do wyniku obliczeń jako publishedBooksMessage.value
. Computed refy są także automatycznie rozpakowywane w szablonach, więc możesz się do nich odwoływać bez .value
w wyrażeniach szablonu.
Właściwość computed automatycznie śledzi swoje reaktywne zależności. Vue wie, że obliczenie publishedBooksMessage
zależy od author.books
, więc zaktualizuje wszystkie powiązania zależne od publishedBooksMessage
, gdy author.books
się zmieni.
Zobacz także: Typowanie właściwości computed
Computed caching vs. metody
Być może zauważyłeś, że ten sam rezultat możemy osiągnąć, wywołując metodę w wyrażeniu:
template
<p>{{ calculateBooksMessage() }}</p>
js
// w komponencie
function calculateBooksMessage() {
return author.books.length > 0 ? 'Tak' : 'Nie'
}
Zamiast właściwości computed możemy zdefiniować tę samą funkcję jako metodę. W ostatecznym wyniku oba podejścia dają dokładnie ten sam efekt. Jednak różnica polega na tym, że właściwości computed są cache'owane na podstawie ich reaktywnych zależności. Właściwość computed zostanie ponownie obliczona tylko wtedy, gdy zmienią się jej reaktywne zależności. Oznacza to, że tak długo, jak author.books
nie ulegnie zmianie, każde kolejne odwołanie do publishedBooksMessage
zwróci natychmiast wcześniej obliczony wynik, bez konieczności ponownego uruchamiania funkcji zwracającej wartość.
To także oznacza, że następująca obliczona właściwość nigdy się nie zaktualizuje, ponieważ Date.now()
nie jest reaktywną zależnością:
js
const now = computed(() => Date.now())
Dla porównania, wywołanie metody zawsze uruchomi funkcję za każdym razem, gdy nastąpi ponowne renderowanie.
Dlaczego potrzebujemy cache'owania? Wyobraź sobie, że mamy kosztowną właściwość computed list
, która wymaga przetworzenia ogromnej tablicy i wykonania wielu operacji obliczeniowych. Jeśli inne obliczone właściwości zależą od list
, to bez cache'owania musielibyśmy uruchamiać funkcję zwracającą wartość list
wiele razy więcej niż jest to konieczne! W przypadkach, w których nie chcesz korzystać z cache'owania, zamiast tego użyj wywołania metody.
Zapisywalne właściwości computed
Obliczone właściwości są domyślnie tylko do odczytu. Jeśli spróbujesz przypisać nową wartość do właściwości computed, otrzymasz ostrzeżenie w czasie działania. W rzadkich przypadkach, gdy potrzebujesz "zapisywalnej" właściwości computed, możesz ją utworzyć, definiując zarówno getter, jak 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) {
// Zwróć uwagę, że używamy tutaj składni destrukturyzacji.
;[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Teraz, gdy wykonasz fullName.value = 'Jan Kowalski'
, wywołany zostanie setter i firstName
oraz lastName
zostaną odpowiednio zaktualizowane.
Pobieranie poprzedniej wartości
- Wsparcie tylko w 3.4+
Jeśli tego potrzebujesz, możesz pobrać poprzednią wartość zwróconą przez computed poprzez pierwszy argument z gettera:
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
// Ten computed zawsze zwraca wartość count gdy jest mniejsza lub równa 3.
// Gdy count >= 4, ostatnia wartość, która spełniła nasz warunek zostanie zwrócona
// dopóki count będzie mniejsze lub większe 3
const alwaysSmall = computed((previous) => {
if (count.value <= 3) {
return count.value
}
return previous
})
</script>
Gdy używasz zapisywalnej wartości computed:
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>
Najlepsze praktyki
Gettery powinny być wolne od efektów ubocznych
Należy pamiętać, że funkcje zwracające wartość właściwości computed powinny wykonywać jedynie czystą kalkulację i być wolne od efektów ubocznych. Nie modyfikuj innych stanów, nie wykonuj asynchronicznych żądań i nie manipuluj DOM-em wewnątrz getterów obliczonych właściwości! Traktuj obliczoną właściwość jako deklaratywny sposób opisania, jak obliczyć wartość na podstawie innych wartości – jej jedynym zadaniem powinno być obliczenie i zwrócenie tej wartości. Później w przewodniku omówimy, jak możemy wykonywać efekty uboczne w reakcji na zmiany stanu za pomocą obserwatorów (watchers).
Unikaj modyfikowania wartości obliczonych
Zwracana wartość z obliczonej właściwości jest stanem pochodnym. Traktuj ją jako tymczasowy zrzut danych – za każdym razem, gdy zmienia się stan źródłowy, tworzony jest nowy zrzut. Modyfikowanie takiego zrzutu nie ma sensu, dlatego wartość zwracana przez obliczoną właściwość powinna być traktowana jako tylko do odczytu i nigdy nie powinna być modyfikowana. Zamiast tego należy aktualizować stan źródłowy, od którego zależy obliczona właściwość, aby wywołać ponowne przeliczenie.