Reactivitate la cerere în Vue 3

Publicat: 2022-03-11

Pe lângă îmbunătățirile admirabile ale performanței, Vue 3 recent lansat a adus și câteva funcții noi. Probabil cea mai importantă introducere este Composition API . În prima parte a acestui articol, recapitulăm motivația standard pentru un nou API: o mai bună organizare și reutilizare a codului. În a doua parte, ne vom concentra asupra aspectelor mai puțin discutate ale utilizării noului API, cum ar fi implementarea caracteristicilor bazate pe reactivitate care erau inexprimabile în sistemul de reactivitate al lui Vue 2.

Ne vom referi la aceasta ca reactivitate la cerere . După introducerea noilor caracteristici relevante, vom construi o aplicație simplă de calcul pentru a demonstra noua expresivitate a sistemului de reactivitate Vue. La final, vom discuta ce utilizare în lumea reală ar putea avea această îmbunătățire a reactivității la cerere.

Ce este nou în Vue 3 și de ce contează

Vue 3 este o rescrie majoră a lui Vue 2, introducând o multitudine de îmbunătățiri, păstrând în același timp compatibilitatea cu vechiul API aproape în întregime.

Una dintre cele mai semnificative funcții noi din Vue 3 este API-ul Composition . Introducerea sa a stârnit multe controverse atunci când a fost discutată pentru prima dată public. În cazul în care nu sunteți deja familiarizat cu noul API, vom descrie mai întâi motivația din spatele acestuia.

Unitatea obișnuită de organizare a codului este un obiect JavaScript ale cărui chei reprezintă diferite tipuri posibile ale unei piese dintr-o componentă. Astfel, obiectul poate avea o secțiune pentru date reactive ( data ), o altă secțiune pentru proprietățile calculate ( computed ), încă una pentru metodele componente ( methods ), etc.

În această paradigmă, o componentă poate avea mai multe funcționalități neînrudite sau vag legate ale căror funcționalități interioare sunt distribuite între secțiunile componente menționate mai sus. De exemplu, am putea avea o componentă pentru încărcarea fișierelor care implementează două funcționalități esențial separate: managementul fișierelor și un sistem care controlează animația stării încărcării.

Porțiunea <script> poate conține ceva de genul următor:

 export default { data () { return { animation_state: 'playing', animation_duration: 10, upload_filenames: [], upload_params: { target_directory: 'media', visibility: 'private', } } }, computed: { long_animation () { return this.animation_duration > 5; }, upload_requested () { return this.upload_filenames.length > 0; }, }, ... }

Există beneficii pentru această abordare tradițională a organizării codului, în principal în cazul în care dezvoltatorul nu trebuie să-și facă griji unde să scrie o nouă bucată de cod. Dacă adăugăm o variabilă reactivă, o inserăm în secțiunea de data . Dacă căutăm o variabilă existentă, știm că trebuie să fie în secțiunea de data .

Această abordare tradițională de împărțire a implementării funcționalității în secțiuni ( data , computed etc.) nu este potrivită în toate situațiile.

Următoarele excepții sunt citate frecvent:

  1. Se ocupă de o componentă cu un număr mare de funcționalități. Dacă vrem să facem upgrade codului nostru de animație cu posibilitatea de a întârzia începerea animației, de exemplu, va trebui să defilăm/sărim între toate secțiunile relevante ale componentei într-un editor de cod. În cazul componentei noastre de încărcare a fișierelor, componenta în sine este mică, iar numărul de funcționalități pe care le implementează este, de asemenea, mic. Astfel, în acest caz, săritul între secțiuni nu este cu adevărat o problemă. Această problemă a fragmentării codului devine relevantă atunci când avem de-a face cu componente mari.
  2. O altă situație în care abordarea tradițională lipsește este reutilizarea codului. Adesea trebuie să facem o combinație specifică de date reactive, proprietăți calculate, metode etc., disponibile în mai mult de o componentă.

Vue 2 (și Vue 3 compatibil cu înapoi) oferă o soluție la majoritatea problemelor de organizare și reutilizare a codului: mixins .

Avantajele și dezavantajele Mixins-urilor în Vue 3

Mixinurile permit extragerea funcționalităților unei componente într-o unitate separată de cod. Fiecare funcționalitate este pusă într-un mixin separat și fiecare componentă poate folosi unul sau mai multe mixine. Piesele definite într-un mixin pot fi utilizate într-o componentă ca și cum ar fi definite în componenta în sine. Mixin-urile sunt un pic ca clasele în limbaje orientate pe obiect, prin faptul că colectează codul legat de o anumită funcționalitate. Ca și clasele, mixin-urile pot fi moștenite (utilizate) în alte unități de cod.

Cu toate acestea, raționamentul cu mixin-uri este mai greu, deoarece, spre deosebire de clase, mixin-urile nu trebuie proiectate având în vedere încapsularea. Mixin-urile au voie să fie colecții de bucăți de cod lex, fără o interfață bine definită cu lumea exterioară. Utilizarea mai multor mixine simultan în aceeași componentă poate avea ca rezultat o componentă dificil de înțeles și utilizat.

Majoritatea limbajelor orientate pe obiecte (de exemplu, C# și Java) descurajează sau chiar interzic moștenirea multiplă, în ciuda faptului că paradigma de programare orientată pe obiecte are instrumentele pentru a face față unei astfel de complexități. (Unele limbi permit moștenirea multiplă, cum ar fi C++, dar compoziția este totuși preferată față de moștenire.)

O problemă mai practică care poate apărea la utilizarea mixin-urilor în Vue este coliziunea de nume, care apare atunci când se utilizează două sau mai multe mixin-uri care declară nume comune. Trebuie remarcat aici că, dacă strategia implicită a Vue pentru a face față coliziunilor de nume nu este ideală într-o situație dată, strategia poate fi ajustată de dezvoltator. Acest lucru vine cu prețul introducerii mai multor complexități.

O altă problemă este că mixin-urile nu oferă ceva asemănător cu un constructor de clasă. Aceasta este o problemă pentru că de multe ori avem nevoie de funcționalități foarte asemănătoare, dar nu exact aceleași, care să fie prezente în diferite componente. Acest lucru poate fi ocolit în unele cazuri simple prin utilizarea fabricilor de mixin.

Prin urmare, mixin-urile nu sunt soluția ideală pentru organizarea și reutilizarea codului, iar cu cât proiectul este mai mare, cu atât problemele lor devin mai serioase. Vue 3 introduce o nouă modalitate de a rezolva aceleași probleme legate de organizarea și reutilizarea codului.

Compoziție API: răspunsul Vue 3 la organizarea și reutilizarea codului

API-ul Composition ne permite (dar nu ne cere) să decuplăm complet piesele unei componente. Fiecare bucată de cod — o variabilă, o proprietate calculată, un ceas etc. — poate fi definită independent.

De exemplu, în loc să avem un obiect care conține o secțiune de data care conține o cheie animation_state cu valoarea (implicit) „playing”, acum putem scrie (oriunde în codul nostru JavaScript):

 const animation_state = ref('playing');

Efectul este aproape același cu declararea acestei variabile în secțiunea de data a unei componente. Singura diferență esențială este că trebuie să facem ca ref definită în afara componentei să fie disponibilă în componenta în care intenționăm să o folosim. Facem acest lucru importând modulul său în locul în care este definită componenta și returnăm ref din secțiunea de setup a unei componente. Vom sări peste această procedură pentru moment și ne vom concentra doar pe noua API pentru un moment. Reactivitatea în Vue 3 nu necesită o componentă; este de fapt un sistem autonom.

Putem folosi variabila animation_state în orice domeniu în care importăm această variabilă. După construirea unui ref , obținem și setăm valoarea sa reală folosind ref.value , de exemplu:

 animation_state.value = 'paused'; console.log(animation_state.value);

Avem nevoie de sufixul „.value” deoarece operatorul de atribuire ar atribui, altfel, valoarea (nereactivă) „paused” variabilei animation_state . Reactivitatea în JavaScript (atât atunci când este implementată prin defineProperty ca în Vue 2, cât și când se bazează pe un Proxy ca în Vue 3) necesită un obiect cu ale cărui chei putem lucra în mod reactiv.

Rețineți că acesta a fost și cazul în Vue 2; acolo, am avut o componentă ca prefix pentru orice membru de date reactiv ( component.data_member ). Cu excepția cazului în care și până când standardul limbajului JavaScript introduce capacitatea de a supraîncărca operatorul de atribuire, expresiile reactive vor necesita un obiect și o cheie (de exemplu, animation_state și value ca mai sus) să apară în partea stângă a oricărei operațiuni de atribuire în care dorim să păstrează reactivitatea.

În șabloane, putem omite .value deoarece Vue trebuie să preproceseze codul șablonului și poate detecta automat referințele:

 <animation :state='animation_state' />

În teorie, compilatorul Vue ar putea preprocesa porțiunea <script> a unei componente de fișier unic (SFC) într-un mod similar, inserând .value acolo unde este necesar. Cu toate acestea, utilizarea refs ar diferi atunci în funcție de dacă folosim SFC-uri sau nu, așa că poate că o astfel de caracteristică nici măcar nu este de dorit.

Uneori, avem o entitate (de exemplu, un obiect Javascript sau o matrice) pe care nu intenționăm niciodată să o înlocuim cu o instanță complet diferită. În schimb, s-ar putea să fim interesați doar de modificarea câmpurilor cu cheie. Există o prescurtare în acest caz: utilizarea reactive în loc de ref ne permite să renunțăm la .value :

 const upload_params = reactive({ target_directory: 'media', visibility: 'private', }); upload_params.visibility = 'public'; // no `.value` needed here // if we did not make `upload_params` constant, the following code would compile but we would lose reactivity after the assignment; it is thus a good idea to make reactive variables ```const``` explicitly: upload_params = { target_directory: 'static', visibility: 'public', };

Reactivitatea decuplată cu ref și reactive nu este o caracteristică complet nouă a Vue 3. A fost introdusă parțial în Vue 2.6, unde astfel de cazuri decuplate de date reactive au fost numite „observabile”. În cea mai mare parte, se poate înlocui Vue.observable cu reactive . Una dintre diferențe este că accesarea și mutarea obiectului transmis direct către Vue.observable este reactivă, în timp ce noul API returnează un obiect proxy, astfel încât mutarea obiectului original nu va avea efecte reactive.

Comparație: API-ul Opțiuni vs. API-ul Compoziție.

Ceea ce este complet nou în Vue 3 este că și alte părți reactive ale unei componente pot fi acum definite independent, pe lângă datele reactive. Proprietățile calculate sunt implementate într-un mod așteptat:

 const x = ref(5); const x_squared = computed(() => x.value * x.value); console.log(x_squared.value); // outputs 25

În mod similar, se pot implementa diferite tipuri de ceasuri, metode de ciclu de viață și injecție de dependență. De dragul conciziei, nu le vom acoperi aici.

Să presupunem că folosim abordarea standard SFC pentru dezvoltarea Vue. S-ar putea chiar să folosim API-ul tradițional, cu secțiuni separate pentru date, proprietăți calculate etc. Cum integrăm micile biți de reactivitate ale API-ului Composition cu SFC-uri? Vue 3 introduce o altă secțiune doar pentru asta: setup . Noua secțiune poate fi gândită ca o nouă metodă de ciclu de viață (care se execută înaintea oricărui alt cârlig, în special, înainte de a fi created ).

Iată un exemplu de componentă completă care integrează abordarea tradițională cu API-ul Composition:

 <template> <input v-model="x" /> <div>Squared: {{ x_squared }}, negative: {{ x_negative }}</div> </template> <script> import { ref, computed } from 'vue'; export default { name: "Demo", computed: { x_negative() { return -this.x; } }, setup() { const x = ref(0); const x_squared = computed(() => x.value * x.value); return {x, x_squared}; } } </script>

Lucruri de reținut din acest exemplu:

  • Tot codul API Composition este acum în setup . Este posibil să doriți să creați un fișier separat pentru fiecare funcționalitate, să importați acest fișier într-un SFC și să returnați biții de reactivitate doriti din setup (pentru a le pune la dispoziție pentru restul componentei).
  • Puteți combina abordarea nouă și cea tradițională în același fișier. Observați că x , chiar dacă este o referință, nu necesită .value atunci când este menționat în codul șablonului sau în secțiunile tradiționale ale unei componente, cum ar fi computed .
  • Nu în ultimul rând, observați că avem două noduri DOM rădăcină în șablonul nostru; capacitatea de a avea mai multe noduri rădăcină este o altă caracteristică nouă a Vue 3.

Reactivitatea este mai expresivă în Vue 3

În prima parte a acestui articol, am atins motivația standard pentru Composition API, care este o organizare și reutilizare îmbunătățită a codului. Într-adevăr, principalul punct de vânzare al noului API nu este puterea sa, ci comoditatea organizațională pe care o aduce: capacitatea de a structura codul mai clar. S-ar putea părea că asta este tot - că API-ul Composition permite o modalitate de implementare a componentelor care evită limitările soluțiilor deja existente, cum ar fi mixinele.

Cu toate acestea, există mai multe în noul API. API-ul Composition permite de fapt nu doar sisteme reactive mai bine organizate, ci și mai puternice. Ingredientul cheie este capacitatea de a adăuga dinamic reactivitate aplicației. Anterior, trebuia să definești toate datele, toate proprietățile calculate etc. înainte de a încărca o componentă. De ce ar fi utilă adăugarea de obiecte reactive într-o etapă ulterioară? În ceea ce a mai rămas, aruncăm o privire asupra unui exemplu mai complex: foile de calcul.

Crearea unei foi de calcul în Vue 2

Instrumentele pentru foi de calcul precum Microsoft Excel, LibreOffice Calc și Google Sheets au toate un fel de sistem de reactivitate. Aceste instrumente prezintă utilizatorului un tabel, cu coloane indexate după A–Z, AA–ZZ, AAA–ZZZ etc. și rânduri indexate numeric.

Fiecare celulă poate conține o valoare simplă sau o formulă. O celulă cu o formulă este în esență o proprietate calculată, care poate depinde de valori sau alte proprietăți calculate. Cu foile de calcul standard (și spre deosebire de sistemul de reactivitate din Vue), aceste proprietăți calculate pot chiar să depindă de ele însele! O astfel de auto-referință este utilă în unele scenarii în care valoarea dorită este obținută prin aproximare iterativă.

Odată ce conținutul unei celule se modifică, toate celulele care depind de celula în cauză vor declanșa o actualizare. Dacă apar modificări suplimentare, este posibil să fie programate actualizări suplimentare.

Dacă ar fi să construim o aplicație pentru foi de calcul cu Vue, ar fi firesc să ne întrebăm dacă putem folosi propriul sistem de reactivitate al lui Vue și să facem din Vue motorul unei aplicații pentru foi de calcul. Pentru fiecare celulă, ne-am putea aminti valoarea brută editabilă, precum și valoarea calculată corespunzătoare. Valorile calculate ar reflecta valoarea brută dacă este o valoare simplă, iar în caz contrar, valorile calculate sunt rezultatul expresiei (formula) care este scrisă în loc de o valoare simplă.

Cu Vue 2, o modalitate de a implementa o foaie de calcul este de a avea raw_values ​​o matrice bidimensională de șiruri, iar computed_values ​​o matrice bidimensională (calculată) de valori de celule.

Dacă numărul de celule este mic și este fix înainte de încărcarea componentei Vue corespunzătoare, am putea avea o valoare brută și o valoare calculată pentru fiecare celulă a tabelului din definiția componentei noastre. În afară de monstruozitatea estetică pe care o astfel de implementare ar provoca-o, un tabel cu un număr fix de celule la momentul compilării probabil nu contează ca o foaie de calcul.

Există probleme și cu matricea bidimensională computed_values ​​. O proprietate calculată este întotdeauna o funcție a cărei evaluare, în acest caz, depinde de ea însăși (calcularea valorii unei celule va necesita, în general, ca alte valori să fie deja calculate). Chiar dacă Vue ar permite proprietăți calculate autoreferențiale, actualizarea unei singure celule ar face ca toate celulele să fie recalculate (indiferent dacă există sau nu dependențe). Acest lucru ar fi extrem de ineficient. Astfel, am putea ajunge să folosim reactivitate pentru a detecta modificările datelor brute cu Vue 2, dar orice altceva din punct de vedere al reactivității ar trebui implementat de la zero.

Modelarea valorilor calculate în Vue 3

Cu Vue 3, putem introduce o nouă proprietate calculată pentru fiecare celulă. Dacă tabelul crește, sunt introduse noi proprietăți calculate.

Să presupunem că avem celulele A1 și A2 și dorim ca A2 să afișeze pătratul lui A1 a cărui valoare este numărul 5. O schiță a acestei situații:

 let A1 = computed(() => 5); let A2 = computed(() => A1.value * A1.value); console.log(A2.value); // outputs 25

Să presupunem că rămânem un moment în acest scenariu simplu. Există o problemă aici; Ce se întâmplă dacă dorim să schimbăm A1 astfel încât să conțină numărul 6? Să presupunem că scriem asta:

 A1 = computed(() => 6); console.log(A2.value); // outputs 25 if we already ran the code above

Acest lucru nu a schimbat doar valoarea de la 5 la 6 în A1 . Variabila A1 are acum o identitate complet diferită: proprietatea calculată care se rezolvă la numărul 6. Cu toate acestea, variabila A2 încă reacţionează la modificările vechii identităţi a variabilei A1 . Deci, A2 nu ar trebui să se refere direct la A1 , ci mai degrabă la un obiect special care va fi întotdeauna disponibil în context și ne va spune ce este A1 în acest moment. Cu alte cuvinte, avem nevoie de un nivel de indirectare înainte de a accesa A1 , ceva ca un pointer. Nu există indicatori ca entități de primă clasă în Javascript, dar este ușor să simulați unul. Dacă dorim să avem un pointer către o value , putem crea un pointer = {points_to: value} . Redirecționarea pointerului înseamnă alocarea către pointer.points_to , iar dereferențiarea (accesarea valorii îndreptate către) înseamnă recuperarea valorii pointer.points_to . În cazul nostru procedăm astfel:

 let A1 = reactive({points_to: computed(() => 5)}); let A2 = reactive({points_to: computed(() => A1.points_to * A1.points_to)}); console.log(A2.points_to); // outputs 25

Acum putem înlocui 5 cu 6.

 A1.points_to = computed(() => 6); console.log(A2.points_to); // outputs 36

Pe serverul Discord de la Vue, utilizatorul redblobgames a sugerat o altă abordare interesantă: în loc să utilizați valori calculate, folosiți referințe care înglobează funcții obișnuite. În acest fel, se poate schimba în mod similar funcția fără a schimba identitatea referinței în sine.

Implementarea foii de calcul va avea celule la care se face referire prin cheile unei matrice bidimensionale. Această matrice poate furniza nivelul de indirectare de care avem nevoie. Astfel, în cazul nostru, nu vom avea nevoie de nicio simulare suplimentară de pointer. Am putea chiar să avem o matrice care să nu facă distincția între valorile brute și cele calculate. Totul poate fi o valoare calculată:

 const cells = reactive([ computed(() => 5), computed(() => cells[0].value * cells[0].value) ]); cells[0] = computed(() => 6); console.log(cells[1].value); // outputs 36

Cu toate acestea, vrem cu adevărat să distingem valorile brute de cele calculate, deoarece dorim să putem lega valoarea brută la un element de intrare HTML. În plus, dacă avem o matrice separată pentru valorile brute, nu trebuie să schimbăm niciodată definițiile proprietăților calculate; se vor actualiza automat pe baza datelor brute.

Implementarea foii de calcul

Să începem cu câteva definiții de bază, care în cea mai mare parte se explică de la sine.

 const rows = ref(30), cols = ref(26); /* if a string codes a number, return the number, else return a string */ const as_number = raw_cell => /^[0-9]+(\.[0-9]+)?$/.test(raw_cell) ? Number.parseFloat(raw_cell) : raw_cell; const make_table = (val = '', _rows = rows.value, _cols = cols.value) => Array(_rows).fill(null).map(() => Array(_cols).fill(val)); const raw_values = reactive(make_table('', rows.value, cols.value)); const computed_values = reactive(make_table(undefined, rows.value, cols.value)); /* a useful metric for debugging: how many times did cell (re)computations occur? */ const calculations = ref(0);

Planul este ca fiecare computed_values[row][column] să fie calculată după cum urmează. Dacă raw_values[row][column] nu începe cu = , returnați raw_values[row][column] . În caz contrar, analizați formula, compilați-o în JavaScript, evaluați codul compilat și returnați valoarea. Pentru a fi scurt, vom înșela puțin cu formulele de analiză și nu vom face niște optimizări evidente aici, cum ar fi un cache de compilare.

Vom presupune că utilizatorii pot introduce orice expresie JavaScript validă ca formulă. Putem înlocui referințele la numele celulelor care apar în expresiile utilizatorului, cum ar fi A1, B5 etc., cu referința la valoarea reală a celulei (calculată). Următoarea funcție îndeplinește această sarcină, presupunând că șirurile care seamănă cu nume de celule într-adevăr identifică întotdeauna celulele (și nu fac parte dintr-o expresie JavaScript care nu are legătură). Pentru simplitate, vom presupune că indicii coloanei constau dintr-o singură literă.

 const letters = Array(26).fill(0) .map((_, i) => String.fromCharCode("A".charCodeAt(0) + i)); const transpile = str => { let cell_replacer = (match, prepend, col, row) => { col = letters.indexOf(col); row = Number.parseInt(row) - 1; return prepend + ` computed_values[${row}][${col}].value `; }; return str.replace(/(^|[^AZ])([AZ])([0-9]+)/g, cell_replacer); };

Folosind funcția transpile , putem obține expresii JavaScript pure din expresiile scrise în mica noastră „extensie” de JavaScript cu referințe de celule.

Următorul pas este generarea proprietăților calculate pentru fiecare celulă. Această procedură va avea loc o dată în viața fiecărei celule. Putem face o fabrică care va returna proprietățile calculate dorite:

 const computed_cell_generator = (i, j) => { const computed_cell = computed(() => { // we don't want Vue to think that the value of a computed_cell depends on the value of `calculations` nextTick(() => ++calculations.value); let raw_cell = raw_values[i][j].trim(); if (!raw_cell || raw_cell[0] != '=') return as_number(raw_cell); let user_code = raw_cell.substring(1); let code = transpile(user_code); try { // the constructor of a Function receives the body of a function as a string let fn = new Function(['computed_values'], `return ${code};`); return fn(computed_values); } catch (e) { return "ERROR"; } }); return computed_cell; }; for (let i = 0; i < rows.value; ++i) for (let j = 0; j < cols.value; ++j) computed_values[i][j] = computed_cell_generator(i, j);

Dacă punem tot codul de mai sus în metoda de setup , trebuie să returnăm {raw_values, computed_values, rows, cols, letters, calculations} .

Mai jos, vă prezentăm componenta completă, împreună cu o interfață de bază cu utilizatorul.

Codul este disponibil pe GitHub și puteți consulta și demonstrația live.

 <template> <div> <div>Calculations: {{ calculations }}</div> <table class="table" border="0"> <tr class="row"> <td></td> <td class="column" v-for="(_, j) in cols" :key="'header' + j" > {{ letters[j] }} </td> </tr> <tr class="row" v-for="(_, i) in rows" :key="i" > <td class="column"> {{ i + 1 }} </td> <td class="column" v-for="(__, j) in cols" :key="i + '-' + j" :class="{ column_selected: active(i, j), column_inactive: !active(i, j), }" @click="activate(i, j)" > <div v-if="active(i, j)"> <input :ref="'input' + i + '-' + j" v-model="raw_values[i][j]" @keydown.enter.prevent="ui_enter()" @keydown.esc="ui_esc()" /> </div> <div v-else v-html="computed_value_formatter(computed_values[i][j].value)"/> </td> </tr> </table> </div> </template> <script> import {ref, reactive, computed, watchEffect, toRefs, nextTick, onUpdated} from "vue"; export default { name: 'App', components: {}, data() { return { ui_editing_i: null, ui_editing_j: null, } }, methods: { get_dom_input(i, j) { return this.$refs['input' + i + '-' + j]; }, activate(i, j) { this.ui_editing_i = i; this.ui_editing_j = j; nextTick(() => this.get_dom_input(i, j).focus()); }, active(i, j) { return this.ui_editing_i === i && this.ui_editing_j === j; }, unselect() { this.ui_editing_i = null; this.ui_editing_j = null; }, computed_value_formatter(str) { if (str === undefined || str === null) return 'none'; return str; }, ui_enter() { if (this.ui_editing_i < this.rows - 1) this.activate(this.ui_editing_i + 1, this.ui_editing_j); else this.unselect(); }, ui_esc() { this.unselect(); }, }, setup() { /*** All the code we wrote above goes here. ***/ return {raw_values, computed_values, rows, cols, letters, calculations}; }, } </script> <style> .table { margin-left: auto; margin-right: auto; margin-top: 1ex; border-collapse: collapse; } .column { box-sizing: border-box; border: 1px lightgray solid; } .column:first-child { background: #f6f6f6; min-width: 3em; } .column:not(:first-child) { min-width: 4em; } .row:first-child { background: #f6f6f6; } #empty_first_cell { background: white; } .column_selected { border: 2px cornflowerblue solid !important; padding: 0px; } .column_selected input, .column_selected input:active, .column_selected input:focus { outline: none; border: none; } </style>

Dar utilizarea în lumea reală?

Am văzut cum sistemul de reactivitate decuplat al Vue 3 permite nu numai un cod mai curat, dar permite sisteme reactive mai complexe bazate pe noul mecanism de reactivitate al lui Vue. Au trecut aproximativ șapte ani de când a fost introdus Vue, iar creșterea expresivității nu a fost în mod clar foarte căutată.

Exemplul de foi de calcul este o demonstrație simplă a ceea ce este capabil Vue acum și puteți consulta, de asemenea, demonstrația live.

Dar, ca exemplu de cuvânt real, este oarecum de nișă. În ce fel de situații ar putea fi util noul sistem? Cel mai evident caz de utilizare pentru reactivitatea la cerere ar putea fi în creșterea performanței pentru aplicații complexe.

Comparația pâlnie Vue 2 vs Vue 3.

În aplicațiile front-end care funcționează cu o cantitate mare de date, suprasolicitarea utilizării reactivității prost gândite poate avea un impact negativ asupra performanței. Să presupunem că avem o aplicație de tablou de bord de afaceri care produce rapoarte interactive ale activității de afaceri a companiei. Utilizatorul poate selecta un interval de timp și poate adăuga sau elimina indicatori de performanță din raport. Unii indicatori pot afișa valori care depind de alți indicatori.

O modalitate de a implementa generarea de rapoarte este printr-o structură monolitică. Când utilizatorul modifică un parametru de intrare în interfață, o singură proprietate calculată, de exemplu, report_data , este actualizată. Calculul acestei proprietăți calculate are loc conform unui plan hardcoded: mai întâi, calculați toți indicatorii de performanță independenți, apoi cei care depind doar de acești indicatori independenți etc.

O implementare mai bună va decupla biți din raport și le va calcula independent. Există câteva beneficii în acest sens:

  • Dezvoltatorul nu trebuie să codifice un plan de execuție, care este obositor și predispus la erori. Sistemul de reactivitate al lui Vue va detecta automat dependențele.
  • În funcție de cantitatea de date implicată, este posibil să obținem câștiguri substanțiale de performanță, deoarece actualizăm doar datele raportului care depind în mod logic de parametrii de intrare modificați.

Dacă toți indicatorii de performanță care pot face parte din raportul final sunt cunoscuți înainte ca componenta Vue să fie încărcată, este posibil să putem implementa decuplarea propusă chiar și cu Vue 2. În caz contrar, dacă backend-ul este singura sursă de adevăr (care este de obicei este cazul aplicațiilor bazate pe date), sau dacă există furnizori externi de date, putem genera proprietăți calculate la cerere pentru fiecare parte dintr-un raport.

Datorită Vue 3, acest lucru este acum nu numai posibil, ci și ușor de realizat.