Reattività in Vue
Pubblicato: 2022-03-10In questo articolo, esamineremo la reattività in Vue, come funziona e come possiamo creare variabili reattive utilizzando metodi e funzioni appena creati. Questo articolo è rivolto agli sviluppatori che hanno una buona conoscenza di come funziona Vue 2.x e stanno cercando di familiarizzare con la nuova Vue 3.
Creeremo una semplice applicazione per comprendere meglio questo argomento. Il codice per questa app può essere trovato su GitHub.
Per impostazione predefinita, JavaScript non è reattivo . Ciò significa che se creiamo la variabile boy
e la referenziamo nella parte A della nostra applicazione, quindi procediamo a modificare boy
nella parte B, la parte A non si aggiornerà con il nuovo valore di boy
.
let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.
Lo snippet sopra è un perfetto esempio della natura non reattiva di JavaScript, quindi, perché la modifica non si riflette nella variabile della sentence
.
In Vue 2.x, props
, computed
e data()
erano tutti reattivi per impostazione predefinita, ad eccezione delle proprietà che non sono presenti nei data
quando vengono creati tali componenti. Ciò significa che quando un componente viene iniettato nel DOM, solo le proprietà esistenti nell'oggetto data
del componente causerebbero l'aggiornamento del componente se e quando tali proprietà cambiano.
Internamente, Vue 3 utilizza l'oggetto Proxy
(una funzionalità ECMAScript 6) per garantire che queste proprietà siano reattive, ma fornisce comunque la possibilità di utilizzare Object.defineProperty
da Vue 2 per il supporto di Internet Explorer (ECMAScript 5). Questo metodo definisce una nuova proprietà direttamente su un oggetto o modifica una proprietà esistente su un oggetto e restituisce l'oggetto.
A prima vista e poiché la maggior parte di noi sa già che la reattività non è nuova in Vue, potrebbe sembrare non necessario utilizzare queste proprietà, ma l'API delle opzioni ha i suoi limiti quando si ha a che fare con una grande applicazione con funzioni riutilizzabili in diversi parti dell'applicazione. A tal fine, è stata introdotta la nuova API di composizione per facilitare l'astrazione della logica al fine di semplificare la lettura e la manutenzione di una base di codice. Inoltre, ora possiamo facilmente rendere reattiva qualsiasi variabile indipendentemente dal tipo di dati utilizzando una qualsiasi delle nuove proprietà e metodi.
Quando utilizziamo l'opzione setup
, che funge da punto di ingresso per l'API Composizione, l'oggetto data
, le proprietà computed
e i methods
sono inaccessibili perché l'istanza del componente non è stata ancora creata quando viene eseguita l' setup
. Ciò rende impossibile sfruttare la reattività incorporata in una qualsiasi di queste funzionalità durante l' setup
. In questo tutorial, impareremo tutti i modi in cui possiamo farlo.
Il metodo reattivo
Secondo la documentazione, il metodo reactive
, che è l'equivalente di Vue.observable()
in Vue 2.6, può essere utile quando si cerca di creare un oggetto le cui proprietà sono tutte reattive (come l'oggetto data
nelle Opzioni API). Sotto il cofano, l'oggetto data
nell'API delle opzioni utilizza questo metodo per rendere reattive tutte le proprietà in esso contenute.
Ma possiamo creare il nostro oggetto reattivo in questo modo:
import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })
Qui, abbiamo importato il metodo reactive
da Vue, quindi abbiamo dichiarato la nostra variabile user
passando il suo valore a questa funzione come argomento. In tal modo, abbiamo reso user
reattivo e, quindi, se utilizziamo user
nel nostro modello e se l'oggetto o una proprietà di questo oggetto dovesse cambiare, questo valore verrà aggiornato automaticamente in questo modello.
ref
Proprio come abbiamo un metodo per rendere reattivi gli oggetti, ne abbiamo bisogno anche per rendere reattivi altri valori primitivi autonomi (stringhe, booleane, valori non definiti, numeri, ecc.) e array. Durante lo sviluppo, lavoreremo con questi altri tipi di dati pur avendo bisogno che siano reattivi. Il primo approccio che potremmo pensare sarebbe usare reactive
e passare il valore della variabile che vogliamo rendere reattiva.
import { reactive } from 'vue' const state = reactive({ users: [], });
Poiché reactive
ha una profonda conversione reattiva, anche user
come proprietà sarebbe reattivo, raggiungendo così il nostro obiettivo; quindi, user
aggiornerebbe sempre ovunque venga utilizzato nel modello di tale app. Ma con la proprietà ref
, possiamo rendere reattiva qualsiasi variabile con qualsiasi tipo di dati passando il valore di quella variabile a ref
. Questo metodo funziona anche per gli oggetti, ma annida l'oggetto a un livello più profondo rispetto a quando viene utilizzato il metodo reactive
.
let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }
Sotto il cofano, ref
prende questo argomento passato e lo converte in un oggetto con una chiave di value
. Ciò significa che possiamo accedere alla nostra variabile chiamando variable.value
e possiamo anche modificarne il valore chiamandolo allo stesso modo.
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
Con questo, possiamo importare ref
nel nostro componente e creare una variabile reattiva:
<template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>
Qui, abbiamo importato ref
per creare una variabile users
reattiva nel nostro componente. Abbiamo quindi importato axios
per recuperare i dati da un file JSON nella cartella public
e abbiamo importato il nostro componente carsNumber
, che creeremo in seguito. La prossima cosa che abbiamo fatto è stata creare una variabile users
reattiva utilizzando il metodo ref
, in modo che users
possano aggiornare ogni volta che la risposta dal nostro file JSON cambia.
Abbiamo anche creato una funzione getUser
che recupera l'array users
dal nostro file JSON usando axios e abbiamo assegnato il valore di questa richiesta alla variabile users
. Infine, abbiamo creato una proprietà calcolata che calcola il numero totale di auto che i nostri utenti hanno in quanto l'abbiamo modificato nella sezione del modello.
È importante notare che quando si accede alle proprietà ref
restituite nella sezione del modello o al di fuori di setup()
, vengono automaticamente scartate. Ciò significa che refs
che sono un oggetto richiederebbero comunque un .value
per potervi accedere. Poiché users
è un array, potremmo semplicemente usare users
e non users.value
in getTotalCars
.
Nella sezione del modello, abbiamo visualizzato una tabella che mostra le informazioni di ciascun utente, insieme a un componente <cars-number />
. Questo componente accetta un supporto per cars
che viene visualizzato nella riga di ogni utente come il numero di auto che ha. Questo valore si aggiorna ogni volta che il valore delle cars
cambia nell'oggetto utente , che è esattamente il modo in cui l'oggetto data
o la proprietà computed
funzionerebbero se stessimo lavorando con l'API delle opzioni.
toRefs
Quando utilizziamo l'API di composizione, la funzione setup
accetta due argomenti: props
e context
. Questa props
viene passata dal componente a setup()
e rende possibile accedere alle props che il componente ha dall'interno di questa nuova API. Questo metodo è particolarmente utile perché consente la destrutturazione degli oggetti senza perdere la sua reattività.
<template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>
Per utilizzare un valore che è un oggetto dagli props
di scena nell'API di composizione assicurandoci che mantenga la sua reattività, utilizziamo toRefs
. Questo metodo prende un oggetto reattivo e lo converte in un oggetto semplice in cui ogni proprietà dell'oggetto reattivo originale diventa un ref
. Ciò significa che le cars
puntano...
cars: { number: 0 }
… ora diventerebbe questo:
{ value: cars: { number: 0 }
Con questo, possiamo utilizzare le cars
all'interno di qualsiasi parte dell'API di configurazione mantenendone comunque la reattività.
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
Possiamo guardare questa nuova variabile usando l' watch
dell'API Composizione e reagire a questo cambiamento come vogliamo.
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
Un altro caso d'uso comune che potremmo incontrare è il passaggio di un valore che non è necessariamente un oggetto ma piuttosto uno dei tipi di dati che funzionano con ref
(array, numero, stringa, booleano, ecc.). Con toRef
, possiamo creare una proprietà reattiva (cioè ref
) da un oggetto reattivo sorgente. In questo modo si assicurerebbe che la proprietà rimanga reattiva e si aggiornerebbe ogni volta che l'origine padre cambia.
const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2
Qui, abbiamo creato un oggetto reattivo utilizzando il metodo reactive
, con le proprietà Toyota
e Honda
. Abbiamo anche utilizzato toRef
per creare una variabile reattiva da Honda
. Dall'esempio sopra, possiamo vedere che quando aggiorniamo Honda
utilizzando l'oggetto cars
reattive o NumberOfHondas
, il valore viene aggiornato in entrambi i casi.
Questo metodo è simile e tuttavia così diverso dal metodo toRefs
di cui abbiamo parlato sopra, nel senso che mantiene la sua connessione alla sua origine e può essere utilizzato per stringhe, array e numeri. A differenza di toRefs
, non dobbiamo preoccuparci dell'esistenza della proprietà nella sua origine al momento della creazione, perché se questa proprietà non esiste al momento della creazione di questo ref
e invece restituisce null
, verrebbe comunque archiviata come proprietà valida, con una forma di watcher
inserita, in modo che quando questo valore cambia, anche questo ref
creato usando toRef
venga aggiornato.
Possiamo anche usare questo metodo per creare una proprietà reattiva da props
. Sarebbe simile a questo:
<template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>
Qui, abbiamo creato un ref
che sarebbe basato sulla proprietà di gender
ottenuta dagli props
di scena. Questo è utile quando vogliamo eseguire operazioni extra sull'elica di un particolare componente.
Conclusione
In questo articolo, abbiamo esaminato come funziona la reattività in Vue utilizzando alcuni dei metodi e delle funzioni appena introdotti da Vue 3. Abbiamo iniziato osservando cos'è la reattività e come Vue utilizza l'oggetto Proxy
dietro le quinte per ottenere questo risultato. Abbiamo anche esaminato come creare oggetti reattivi usando reactive
e come creare proprietà reattive usando ref
.
Infine, abbiamo visto come convertire oggetti reattivi in oggetti semplici, ciascuna delle cui proprietà è un ref
che punta alla proprietà corrispondente dell'oggetto originale, e abbiamo visto come creare un ref
per una proprietà su un oggetto sorgente reattivo.
Ulteriori risorse
- "Proxy" (oggetto), MDN Web Docs
- "Fondamenti di reattività", Vue.js
- "Riferimenti", Vue.js
- "
setup
della registrazione di Lifecycle Hook all'interno", Vue.js