Reactivitate în Vue
Publicat: 2022-03-10În acest articol, vom analiza reactivitatea în Vue, cum funcționează și cum putem crea variabile reactive folosind metode și funcții nou create. Acest articol este destinat dezvoltatorilor care au o bună înțelegere a modului în care funcționează Vue 2.x și caută să se familiarizeze cu noul Vue 3.
Vom construi o aplicație simplă pentru a înțelege mai bine acest subiect. Codul pentru această aplicație poate fi găsit pe GitHub.
În mod implicit, JavaScript nu este reactiv . Aceasta înseamnă că, dacă creăm variabila boy
și o facem referință în partea A a aplicației noastre, atunci trecem la modificarea boy
în partea B, partea A nu se va actualiza cu noua valoare a 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.
Fragmentul de mai sus este un exemplu perfect al naturii nereactive a JavaScript - prin urmare, de ce modificarea nu este reflectată în variabila sentence
.
În Vue 2.x, props
, computed
și data()
au fost toate reactive în mod implicit, cu excepția proprietăților care nu sunt prezente în data
atunci când sunt create astfel de componente. Aceasta înseamnă că, atunci când o componentă este injectată în DOM, numai proprietățile existente în obiectul de data
al componentei ar determina actualizarea componentei dacă și când astfel de proprietăți se schimbă.
Pe plan intern, Vue 3 utilizează obiectul Proxy
(o caracteristică ECMAScript 6) pentru a se asigura că aceste proprietăți sunt reactive, dar oferă totuși opțiunea de a utiliza Object.defineProperty
din Vue 2 pentru suport pentru Internet Explorer (ECMAScript 5). Această metodă definește o proprietate nouă direct pe un obiect sau modifică o proprietate existentă pe un obiect și returnează obiectul.
La prima vedere și din moment ce majoritatea dintre noi știm deja că reactivitatea nu este nouă în Vue, ar putea părea inutil să folosiți aceste proprietăți, dar API-ul Options are limitările sale atunci când aveți de-a face cu o aplicație mare cu funcții reutilizabile în mai multe părți ale aplicației. În acest scop, a fost introdus noul Composition API pentru a ajuta la abstractizarea logicii pentru a face o bază de cod mai ușor de citit și de întreținut. De asemenea, acum putem face cu ușurință orice variabilă reactivă, indiferent de tipul ei de date, folosind oricare dintre noile proprietăți și metode.
Când folosim opțiunea de setup
, care servește drept punct de intrare pentru API-ul Composition, obiectul de data
, proprietățile computed
și methods
sunt inaccesibile, deoarece instanța componentei nu a fost încă creată când se execută setup
. Acest lucru face imposibil să profitați de reactivitatea încorporată în oricare dintre aceste caracteristici în setup
. În acest tutorial, vom afla despre toate modurile în care putem face acest lucru.
Metoda reactivă
Conform documentației, metoda reactive
, care este echivalentul lui Vue.observable()
în Vue 2.6, poate fi utilă atunci când încercăm să creăm un obiect ale cărui proprietăți sunt reactive (cum ar fi obiectul de data
din Opțiuni). API). Sub capotă, obiectul de data
din API-ul Opțiuni utilizează această metodă pentru a face reactive toate proprietățile din acesta.
Dar ne putem crea propriul obiect reactiv astfel:
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 } })
Aici, am importat metoda reactive
din Vue și apoi am declarat variabila user
, pasând valoarea acesteia acestei funcție ca argument. Făcând acest lucru, am făcut user
reactiv și, prin urmare, dacă folosim user
în șablonul nostru și dacă obiectul sau o proprietate a acestui obiect ar trebui să se modifice, atunci această valoare va fi actualizată automat în acest șablon.
ref
Așa cum avem o metodă de a face obiectele reactive, avem nevoie și de una pentru a face reactive alte valori primitive de sine stătătoare (șiruri, booleeni, valori nedefinite, numere etc.) și matrice reactive. În timpul dezvoltării, am lucra cu aceste alte tipuri de date, în timp ce avem nevoie de ele pentru a fi reactive. Prima abordare la care ne-am putea gândi ar fi să folosim reactive
și să trecem valoarea variabilei pe care dorim să o facem reactivă.
import { reactive } from 'vue' const state = reactive({ users: [], });
Deoarece reactive
are o conversie reactivă profundă, user
ca proprietate ar fi, de asemenea, reactiv, atingându-ne astfel obiectivul; prin urmare, user
ar actualiza întotdeauna oriunde este folosit în șablonul unei astfel de aplicații. Dar cu proprietatea ref
, putem face reactivă orice variabilă cu orice tip de date, trecând valoarea acelei variabile la ref
. Această metodă funcționează și pentru obiecte, dar cuibează obiectul cu un nivel mai adânc decât atunci când este utilizată metoda 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} // }
Sub capotă, ref
preia acest argument care ia fost transmis și îl convertește într-un obiect cu o cheie de value
. Aceasta înseamnă că putem accesa variabila noastră apelând variable.value
și, de asemenea, îi putem modifica valoarea apelând-o în același mod.
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
Cu aceasta, putem importa ref
în componenta noastră și putem crea o variabilă reactivă:
<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>
Aici, am importat ref
pentru a crea o variabilă users
reactivi în componenta noastră. Am importat apoi axios
pentru a prelua date dintr-un fișier JSON din folderul public
și am importat componenta carsNumber
, pe care o vom crea mai târziu. Următorul lucru pe care l-am făcut a fost să creăm o variabilă users
reactivi folosind metoda ref
, astfel încât users
să poată actualiza ori de câte ori răspunsul din fișierul nostru JSON se modifică.
De asemenea, am creat o funcție getUser
care preia matricea users
din fișierul nostru JSON folosind axios și am atribuit valoarea din această solicitare variabilei users
. În cele din urmă, am creat o proprietate calculată care calculează numărul total de mașini pe care le au utilizatorii noștri, așa cum am modificat-o în secțiunea șabloane.
Este important de reținut că atunci când accesați proprietățile ref
care sunt returnate în secțiunea șablon sau în afara setup()
, acestea sunt automat dezvăluite superficial. Aceasta înseamnă că .value
refs
a fi accesate. Deoarece users
sunt o matrice, am putea folosi pur și simplu users
și nu users.value
în getTotalCars
.
În secțiunea șabloane, am afișat un tabel care afișează informațiile fiecărui utilizator, împreună cu o componentă <cars-number />
. Această componentă acceptă un suport de cars
care este afișat în rândul fiecărui utilizator ca număr de mașini pe care le are. Această valoare se actualizează ori de câte ori valoarea cars
se modifică în obiectul utilizator , ceea ce este exact cum ar funcționa obiectul de data
sau proprietatea computed
dacă am lucra cu API-ul Opțiuni.
toRefs
Când folosim API-ul Composition, funcția de setup
acceptă două argumente: props
și context
. Această props
este transmisă de la componentă la setup()
și face posibilă accesarea elementelor de recuzită pe care le are componenta din interiorul acestui nou API. Această metodă este deosebit de utilă deoarece permite destructurarea obiectelor fără a-și pierde reactivitatea.
<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>
Pentru a folosi o valoare care este un obiect din elemente de props
în API-ul Composition, asigurându-ne în același timp că își menține reactivitatea, folosim toRefs
. Această metodă ia un obiect reactiv și îl convertește într-un obiect simplu în care fiecare proprietate a obiectului reactiv original devine un ref
. Ce înseamnă asta este că cars
sprijină...
cars: { number: 0 }
… ar deveni acum aceasta:
{ value: cars: { number: 0 }
Prin aceasta, putem folosi cars
în orice parte a API-ului de configurare, menținând în același timp reactivitatea.
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
Putem urmări această nouă variabilă folosind watch
API-ului Composition și putem reacționa la această schimbare oricum am dori.
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
Un alt caz de utilizare comun cu care ne-am putea confrunta este transmiterea unei valori care nu este neapărat un obiect, ci mai degrabă unul dintre tipurile de date care funcționează cu ref
(matrice, număr, șir, boolean etc.). Cu toRef
, putem crea o proprietate reactivă (adică ref
) dintr-un obiect reactiv sursă. Procedând astfel, proprietatea rămâne reactivă și se va actualiza ori de câte ori sursa părinte se schimbă.
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
Aici, am creat un obiect reactiv folosind metoda reactive
, cu proprietățile Toyota
și Honda
. De asemenea, am folosit toRef
pentru a crea o variabilă reactivă din Honda
. Din exemplul de mai sus, putem vedea că atunci când actualizăm Honda
folosind fie obiectul cars
reactive, fie NumberOfHondas
, valoarea este actualizată în ambele cazuri.
Această metodă este similară și totuși atât de diferită de metoda toRefs
pe care am tratat-o mai sus, în sensul că își menține conexiunea cu sursa și poate fi folosită pentru șiruri, matrice și numere. Spre deosebire de toRefs
, nu trebuie să ne îngrijorăm cu privire la existența proprietății în sursa sa la momentul creării, deoarece dacă această proprietate nu există la momentul creării acestui ref
și în schimb returnează null
, ea ar fi în continuare stocată. ca o proprietate validă, cu o formă de watcher
pusă în aplicare, astfel încât atunci când această valoare se schimbă, acest ref
creat folosind toRef
să fie, de asemenea, actualizat.
De asemenea, putem folosi această metodă pentru a crea o proprietate reactivă din elemente de props
. Asta ar arata asa:
<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>
Aici, am creat o ref
care se va baza pe proprietatea de gender
obținută de la props
. Acest lucru este util atunci când dorim să efectuăm operații suplimentare pe suportul unei anumite componente.
Concluzie
În acest articol, ne-am uitat la modul în care funcționează reactivitatea în Vue utilizând unele dintre metodele și funcțiile recent introduse din Vue 3. Am început prin a ne uita la ce este reactivitatea și la modul în care Vue folosește obiectul Proxy
în culise pentru a realiza acest lucru. Am analizat, de asemenea, cum putem crea obiecte reactive folosind reactive
și cum să creăm proprietăți reactive folosind ref
.
În cele din urmă, ne-am uitat la cum să convertim obiecte reactive în obiecte simple, fiecare dintre proprietățile cărora sunt un ref
care indică proprietatea corespunzătoare a obiectului original și am văzut cum să creăm un ref
pentru o proprietate pe un obiect sursă reactiv.
Resurse suplimentare
- „Proxy” (obiect), MDN Web Docs
- „Elementele fundamentale ale reactivității”, Vue.js
- „Refs”, Vue.js
- „Lifecycle Hook Registration Inside
setup
”, Vue.js