Reactivitate în Vue

Publicat: 2022-03-10
Rezumat rapid ↬ Reactivitatea este capacitatea unei variabile (matrice, șir, număr, obiect etc.) de a se actualiza atunci când valoarea acesteia sau orice altă variabilă la care face referire este modificată după declarare.

Î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.

Mai multe după săritură! Continuați să citiți mai jos ↓

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