Właściwości obliczane (Computed Properties)
Przykład podstawowy
Wyrażenia w szablonach są bardzo wygodne, ale służą do wykonywania prostych operacji. Umieszczanie zbyt dużej ilości logiki w szablonach może spowodować, że staną się one obszerne 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'
]
})
Chcemy też wyświetlać różne komunikaty w zależności od tego, czy autor
ma już jakieś książki, czy nie:
template
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
W tym momencie szablon staje się nieco zagracony. Musimy przyjrzeć się mu przez chwilę, aby zorientować się, że wykonuje on obliczenia w zależności od author.books
. Co ważniejsze, prawdopodobnie nie chcemy się powtarzać, jeśli będziemy musieli zawrzeć to obliczenie w szablonie więcej niż jeden raz.
Dlatego właśnie w przypadku złożonej logiki, która zawiera dane reaktywne, zaleca się używanie właściwości computed. Poniżej znajduje się ten sam przykład po refaktoryzacji:
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'
]
})
// ref obliczeniowy
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Tutaj zadeklarowaliśmy computed property (właściwość obliczaną) publishedBooksMessage
. Funkcja computed()
oczekuje, że zostanie jej przekazana funkcja pobierająca, a zwróconą wartością jest computed ref. Podobnie jak w przypadku zwykłych refów, możesz uzyskać dostęp do obliczonego wyniku jako publishedBooksMessage.value
. Obliczone referencje są również automatycznie rozpakowywane w szablonach, więc można się do nich odwoływać bez .value
w wyrażeniach szablonów.
Obliczona właściwość automatycznie śledzi swoje reaktywne zależności. Vue jest świadome, że obliczenie publishedBooksMessage
zależy od author.books
, więc zaktualizuje wszystkie powiązania, które zależą od publishedBooksMessage
, gdy author.books
się zmieni.
Zobacz także: Typowanie Computed Properties
Buforowanie Computed a 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 ? 'Yes' : 'No'
}
Zamiast właściwości obliczanej możemy zdefiniować tę samą funkcję jako metodę. Jeśli chodzi o wynik końcowy, oba podejścia są w istocie dokładnie takie same. Różnica polega jednak na tym, że właściwości obliczeniowe są buforowane w oparciu o ich reaktywne zależności. Właściwość obliczeniowa będzie ponownie oceniana tylko wtedy, gdy niektóre z jej reaktywnych zależności ulegną zmianie. Oznacza to, że dopóki author.books
nie ulegnie zmianie, wielokrotny dostęp do publishedBooksMessage
natychmiast zwróci poprzednio obliczony wynik bez konieczności ponownego uruchamiania funkcji gettera.
Oznacza to również, że poniższa właściwość obliczeniowa nigdy się nie zaktualizuje, ponieważ Date.now()
nie jest zależnością reaktywną:
js
const now = computed(() => Date.now())
Dla porównania, wywołanie metody spowoduje zawsze uruchomienie funkcji, gdy tylko nastąpi renderowanie.
Dlaczego potrzebujemy buforowania? Wyobraźmy sobie, że mamy kosztowną właściwość obliczeniową list
, która wymaga zapętlenia ogromnej tablicy i wykonania wielu obliczeń. Następnie możemy mieć inne obliczane właściwości, które z kolei zależą od listy
. Bez buforowania, wykonywalibyśmy getter list
o wiele więcej razy niż to konieczne! W przypadkach, gdy nie chcesz buforować, użyj zamiast tego wywołania metody.
Zapisywalne Computed
Właściwości obliczane są domyślnie typu getter-only. Jeśli spróbujesz przypisać nową wartość do właściwości obliczeniowej, otrzymasz ostrzeżenie w czasie wykonywania. W rzadkich przypadkach, gdy potrzebna jest "zapisywalna" właściwość obliczana, można ją utworzyć, podają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) {
// Uwaga: używamy tutaj składni przypisania destrukturyzacji.
;[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Teraz, gdy uruchomisz this.fullName = 'John Doe'
, setter zostanie wywołany, a this.firstName
i this.lastName
zostaną odpowiednio zaktualizowane.
Najlepsze praktyki
Gettery powinny być wolne od efektów ubocznych
Ważne jest, aby pamiętać, że obliczeniowe funkcje pobierające powinny wykonywać tylko czyste obliczenia i być wolne od efektów ubocznych. Na przykład, nie wykonuj asynchronicznych żądań ani nie mutuj DOM wewnątrz obliczeniowego gettera! Pomyśl o właściwości computed jako o deklaratywnym opisie sposobu uzyskiwania wartości na podstawie innych wartości - jej jedynym zadaniem powinno być obliczenie i zwrócenie tej wartości. W dalszej części przewodnika omówimy, w jaki sposób możemy wykonywać efekty uboczne w reakcji na zmiany stanu za pomocą watchers.
Unikaj modyfikacji wartości obliczanej
Wartość zwracana przez obliczaną właściwość jest stanem pochodnym. Pomyśl o nim jak o tymczasowej migawce - za każdym razem, gdy zmienia się stan źródłowy, tworzona jest nowa migawka. Modyfikowanie migawki nie ma sensu, dlatego wartość zwracana przez właściwość obliczaną powinna być traktowana jako tylko do odczytu i nigdy nie powinna być modyfikowana - zamiast tego należy zaktualizować stan źródłowy, od którego zależy, aby wywołać nowe obliczenia.