Computed Properties
Basic Example
Le espressioni all'interno dei template sono molto comode, ma sono pensate per operazioni semplici. Inserire troppa logica nei tuoi template può renderli inutilmente lunghi e difficili da mantenere. Ad esempio, se abbiamo un oggetto con un array annidato:
js
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
E se vogliamo mostrare messaggi diversi a seconda che author
abbia o meno alcuni libri:
template
<p>Ha pubblicato dei libri:</p>
<span>{{ author.books.length > 0 ? 'Si' : 'No' }}</span>
A questo punto il template sta diventando un po' disordinato. Dobbiamo guardarlo per un attimo prima di renderci conto che esegue una valutazione riguardo a author.books
. In oltre, probabilmente non vogliamo ripeterci se abbiamo bisogno di includere questa valutazione nel template più di una volta.
Ecco perché, per la logica complessa che include dati reattivi, si consiglia di utilizzare una computed property (proprietà calcolata). Ecco lo stesso esempio, riscritto:
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'
]
})
// a computed ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Si' : 'No'
})
</script>
<template>
<p>Ha pubblicato dei libri:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Qui abbiamo dichiarato una computed property publishedBooksMessage
. La funzione computed()
si aspetta di ricevere una funzione getter, e il valore restituito è un computed ref. Similmente ai normali ref, puoi accedere al risultato calcolato con publishedBooksMessage.value
. I computed ref vengono anche estratti automaticamente nei template, quindi puoi utilizzarli senza .value
nelle espressioni del template.
Una computed property tiene traccia automaticamente delle sue dipendenze reattive. Vue è consapevole che il calcolo di publishedBooksMessage
dipende da author.books
, quindi aggiornerà qualsiasi binding che dipende da publishedBooksMessage
quando author.books
cambia.
Vedi anche: Tipizzazione delle Computed Properties
Computed Caching vs. Metodi
Potresti aver notato che possiamo ottenere lo stesso risultato invocando un metodo nell'espressione:
template
<p>{{ calculateBooksMessage() }}</p>
js
// in component
function calculateBooksMessage() {
return author.books.length > 0 ? 'Yes' : 'No'
}
Al posto di una computed property possiamo definire la stessa funzione come un metodo. I due approcci sono effettivamente gli stessi e producono esattamente lo stesso risultato finale. Tuttavia, la differenza è che le computed properties vengono memorizzate nella cache in base alle loro dipendenze reattive. Una computed property verrà riesaminata solo quando almeno una delle sue dipendenze reattive viene modificata. Ciò significa che finché author.books
non verrà modificato, gli accessi successivi a publishedBooksMessage
restituiranno sempre e immediatamente il risultato calcolato in precedenza, senza dover eseguire nuovamente la funzione getter.
Ciò significa anche che la seguente computed property non verrà mai aggiornata, perché Date.now()
non è una dipendenza reattiva:
js
const now = computed(() => Date.now())
Al contrario, l'invocazione di un metodo eseguirà sempre la funzione, ogni volta che si verifica una renderizzazione.
Perché abbiamo bisogno della cache? Immagina di avere una computed property list
complicata, che richiede di scorrere un enorme array e di effettuare molti calcoli. E potremmo avere altre computed properties che dipendono a loro volta da list
. Senza la cache, eseguiremmo il getter di list
molte più volte del necessario! Nei casi in cui non si desidera la cache, si può invece utilizzare un metodo.
Computed Modificabili
Le computed properties sono usate di default solo come getter. Se provi ad assegnare un nuovo valore a una computed property riceverai un avviso di runtime. Nei rari casi in cui hai bisogno di una computed property "scrivibile", puoi crearne una fornendo sia un getter che un 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) {
// Nota: qui stiamo utilizzando la sintassi di assegnazione tramite destrutturazione.
[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Ora, quando esegui fullName.value = 'John Doe'
, il setter verrà invocato e firstName
e lastName
saranno aggiornati di conseguenza.
Best Practices
I Getter dovrebbero essere privi di effetti collaterali
È importante ricordare che le funzioni getter delle computed dovrebbero eseguire solo calcoli puri ed essere privi di effetti collaterali. Ad esempio, non effettuare richieste asincrone o modificare il DOM all'interno di un getter di una computed! Pensa a una computed property come a una descrizione dichiarativa di come ottenere un valore basandosi su altri valori - la sua unica responsabilità dovrebbe essere calcolare e restituire quel valore. Più avanti nella guida discuteremo di come possiamo ottenere consapevolmente degli effetti collaterali in risposta ai cambiamenti di stato con i watchers.
Evita di modificare il valore computed
Il valore restituito da una computed property è derivato dallo stato. Pensalo come a uno snapshot temporaneo - ogni volta che lo stato sorgente cambia, viene creato un nuovo snapshot. Non ha senso modificare uno snapshot, quindi, un valore restituito da una computed dovrebbe essere trattato come un valore di sola lettura e non dovrebbe essere mai modificato - aggiorna, invece, lo stato sorgente da cui dipende, per provocare nuovi calcoli.