Reattività su richiesta in Vue 3

Pubblicato: 2022-03-11

Oltre ad ammirevoli miglioramenti delle prestazioni, Vue 3 recentemente rilasciato ha portato anche diverse nuove funzionalità. Probabilmente l'introduzione più importante è l' API di composizione . Nella prima parte di questo articolo, ricapitoliamo la motivazione standard per una nuova API: una migliore organizzazione del codice e riutilizzo. Nella seconda parte, ci concentreremo sugli aspetti meno discussi dell'utilizzo della nuova API, come l'implementazione di funzionalità basate sulla reattività che erano inesprimibili nel sistema di reattività di Vue 2.

Ci riferiremo a questo come reattività su richiesta . Dopo aver introdotto le nuove funzionalità rilevanti, costruiremo una semplice applicazione per fogli di calcolo per dimostrare la nuova espressività del sistema di reattività di Vue. Alla fine, discuteremo quale utilizzo nel mondo reale potrebbe avere questo miglioramento della reattività su richiesta.

Cosa c'è di nuovo in Vue 3 e perché è importante

Vue 3 è un'importante riscrittura di Vue 2, che introduce una miriade di miglioramenti pur mantenendo la compatibilità con le versioni precedenti con la vecchia API quasi nella sua interezza.

Una delle novità più significative di Vue 3 è l' API di composizione . La sua introduzione ha suscitato molte polemiche quando è stata discussa pubblicamente per la prima volta. Nel caso in cui non hai già familiarità con la nuova API, descriveremo prima la motivazione alla base.

La solita unità di organizzazione del codice è un oggetto JavaScript le cui chiavi rappresentano vari tipi possibili di una parte di un componente. Pertanto l'oggetto potrebbe avere una sezione per i dati reattivi ( data ), un'altra sezione per le proprietà calcolate ( computed ), un'altra per i metodi dei componenti ( methods ), ecc.

In questo paradigma, un componente può avere più funzionalità non correlate o vagamente correlate i cui meccanismi interni sono distribuiti tra le suddette sezioni dei componenti. Ad esempio, potremmo avere un componente per il caricamento dei file che implementa due funzionalità essenzialmente separate: la gestione dei file e un sistema che controlla l'animazione dello stato del caricamento.

La parte <script> potrebbe contenere qualcosa di simile al seguente:

 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; }, }, ... }

Ci sono vantaggi in questo approccio tradizionale all'organizzazione del codice, principalmente nel fatto che lo sviluppatore non deve preoccuparsi di dove scrivere un nuovo pezzo di codice. Se stiamo aggiungendo una variabile reattiva, la inseriamo nella sezione dei data . Se stiamo cercando una variabile esistente, sappiamo che deve trovarsi nella sezione dei data .

Questo approccio tradizionale di suddividere l'implementazione della funzionalità in sezioni ( data , computed , ecc.) non è adatto in tutte le situazioni.

Vengono spesso citate le seguenti eccezioni:

  1. Trattare con un componente con un gran numero di funzionalità. Se vogliamo aggiornare il nostro codice di animazione con la possibilità di ritardare l'inizio dell'animazione, ad esempio, dovremo scorrere/saltare tra tutte le sezioni rilevanti del componente in un editor di codice. Nel caso del nostro componente di caricamento file, il componente stesso è piccolo e anche il numero di funzionalità che implementa è piccolo. Quindi, in questo caso, saltare tra le sezioni non è davvero un problema. Questo problema della frammentazione del codice diventa rilevante quando abbiamo a che fare con componenti di grandi dimensioni.
  2. Un'altra situazione in cui manca l'approccio tradizionale è il riutilizzo del codice. Spesso è necessario rendere disponibile una specifica combinazione di dati reattivi, proprietà calcolate, metodi, ecc. in più di un componente.

Vue 2 (e Vue 3 compatibile con le versioni precedenti) offrono una soluzione alla maggior parte dei problemi di organizzazione del codice e riutilizzo: mixins .

Pro e contro dei mixin in Vue 3

I mixin consentono di estrarre le funzionalità di un componente in un'unità di codice separata. Ogni funzionalità viene inserita in un mixin separato e ogni componente può utilizzare uno o più mixin. I pezzi definiti in un mixin possono essere utilizzati in un componente come se fossero definiti nel componente stesso. I mixin sono un po' come le classi nei linguaggi orientati agli oggetti in quanto raccolgono il codice relativo a una determinata funzionalità. Come le classi, i mixin possono essere ereditati (usati) in altre unità di codice.

Tuttavia, il ragionamento con i mixin è più difficile poiché, a differenza delle classi, i mixin non devono essere progettati pensando all'incapsulamento. I mixin possono essere raccolte di pezzi di codice liberamente legati senza un'interfaccia ben definita con il mondo esterno. L'utilizzo di più di un mixin alla volta nello stesso componente potrebbe risultare in un componente difficile da comprendere e utilizzare.

La maggior parte dei linguaggi orientati agli oggetti (ad es. C# e Java) scoraggiano o addirittura impediscono l'ereditarietà multipla nonostante il paradigma di programmazione orientato agli oggetti abbia gli strumenti per affrontare tale complessità. (Alcuni linguaggi consentono l'ereditarietà multipla, come C++, ma la composizione è comunque preferita all'ereditarietà.)

Un problema più pratico che può verificarsi quando si utilizzano i mixin in Vue è la collisione dei nomi, che si verifica quando si utilizzano due o più mixin che dichiarano nomi comuni. Va notato qui che se la strategia predefinita di Vue per gestire le collisioni di nomi non è l'ideale in una determinata situazione, la strategia può essere modificata dallo sviluppatore. Ciò ha il costo di introdurre maggiore complessità.

Un altro problema è che i mixin non offrono qualcosa di simile a un costruttore di classi. Questo è un problema perché spesso abbiamo bisogno di funzionalità molto simili, ma non esattamente uguali, per essere presenti in componenti diversi. Questo può essere aggirato in alcuni casi semplici con l'uso di fabbriche di mixin.

Pertanto, i mixin non sono la soluzione ideale per l'organizzazione e il riutilizzo del codice e più grande è il progetto, più seri diventano i loro problemi. Vue 3 introduce un nuovo modo per risolvere gli stessi problemi relativi all'organizzazione e al riutilizzo del codice.

API di composizione: la risposta di Vue 3 all'organizzazione e al riutilizzo del codice

L'API di composizione ci consente (ma non ci richiede) di disaccoppiare completamente i pezzi di un componente. Ogni pezzo di codice, una variabile, una proprietà calcolata, un orologio, ecc., può essere definito in modo indipendente.

Ad esempio, invece di avere un oggetto che contiene una sezione di data che contiene una chiave animation_state con il valore (predefinito) "riproduzione", ora possiamo scrivere (ovunque nel nostro codice JavaScript):

 const animation_state = ref('playing');

L'effetto è quasi lo stesso della dichiarazione di questa variabile nella sezione dei data di qualche componente. L'unica differenza essenziale è che dobbiamo rendere disponibile il ref definito al di fuori del componente nel componente in cui intendiamo usarlo. Lo facciamo importando il suo modulo nel punto in cui è definito il componente e restituiamo il ref dalla sezione di setup di un componente. Salteremo questa procedura per ora e ci concentreremo solo sulla nuova API per un momento. La reattività in Vue 3 non richiede un componente; in realtà è un sistema autonomo.

Possiamo usare la variabile animation_state in qualsiasi ambito in cui importiamo questa variabile. Dopo aver costruito un ref , otteniamo e impostiamo il suo valore effettivo usando ref.value , ad esempio:

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

Abbiamo bisogno del suffisso '.value' poiché l'operatore di assegnazione assegnerebbe altrimenti il ​​valore (non reattivo) "in pausa" alla variabile animation_state . La reattività in JavaScript (sia quando è implementata tramite defineProperty come in Vue 2, sia quando è basata su un Proxy come in Vue 3) richiede un oggetto con le cui chiavi possiamo lavorare in modo reattivo.

Nota che questo era il caso anche in Vue 2; lì, avevamo un componente come prefisso per qualsiasi membro di dati reattivo ( component.data_member ). A meno che e fino a quando lo standard del linguaggio JavaScript non introduca la capacità di sovraccaricare l'operatore di assegnazione, le espressioni reattive richiederanno che un oggetto e una chiave (ad es. animation_state e value come sopra) appaiano sul lato sinistro di qualsiasi operazione di assegnazione in cui desideriamo preservare la reattività.

Nei modelli, possiamo omettere .value poiché Vue deve preelaborare il codice del modello e può rilevare automaticamente i riferimenti:

 <animation :state='animation_state' />

In teoria, il compilatore Vue potrebbe anche preelaborare la porzione <script> di un Single File Component (SFC) in modo simile, inserendo .value dove necessario. Tuttavia, l'uso dei refs differirebbe in base al fatto che utilizziamo o meno gli SFC, quindi forse una tale funzionalità non è nemmeno desiderabile.

A volte, abbiamo un'entità (ad esempio, essere un oggetto Javascript o un array) che non intendiamo mai sostituire con un'istanza completamente diversa. Invece, potremmo essere interessati solo a modificare i suoi campi con chiave. C'è una scorciatoia in questo caso: l'uso di reactive invece di ref ci consente di fare a meno del .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', };

La reattività disaccoppiata con ref e reactive non è una funzionalità completamente nuova di Vue 3. È stata parzialmente introdotta in Vue 2.6, dove tali istanze disaccoppiate di dati reattivi erano chiamate "osservabili". Per la maggior parte, è possibile sostituire Vue.observable con reactive . Una delle differenze è che l'accesso e la mutazione dell'oggetto passato direttamente a Vue.observable è reattivo, mentre la nuova API restituisce un oggetto proxy, quindi la mutazione dell'oggetto originale non avrà effetti reattivi.

Confronto: API delle opzioni e API di composizione.

La novità assoluta di Vue 3 è che ora è possibile definire indipendentemente anche altri pezzi reattivi di un componente, oltre ai dati reattivi. Le proprietà calcolate sono implementate in modo previsto:

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

Allo stesso modo, è possibile implementare vari tipi di orologi, metodi del ciclo di vita e iniezione di dipendenze. Per brevità, non li tratteremo qui.

Supponiamo di utilizzare l'approccio SFC standard allo sviluppo di Vue. Potremmo anche utilizzare l'API tradizionale, con sezioni separate per dati, proprietà calcolate, ecc. Come integriamo i piccoli bit di reattività dell'API di composizione con gli SFC? Vue 3 introduce un'altra sezione proprio per questo: setup . La nuova sezione può essere considerata come un nuovo metodo del ciclo di vita (che viene eseguito prima di qualsiasi altro hook, in particolare prima della created ).

Ecco un esempio di un componente completo che integra l'approccio tradizionale con la Composition API:

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

Cose da prendere da questo esempio:

  • Tutto il codice dell'API di composizione è ora setup . Potresti voler creare un file separato per ciascuna funzionalità, importare questo file in un SFC e restituire i bit di reattività desiderati setup (per renderli disponibili al resto del componente).
  • Puoi combinare il nuovo e l'approccio tradizionale nello stesso file. Si noti che x , anche se è un riferimento, non richiede .value quando si fa riferimento nel codice del modello o nelle sezioni tradizionali di un componente come computed .
  • Ultimo ma non meno importante, nota che abbiamo due nodi DOM radice nel nostro modello; la possibilità di avere più nodi radice è un'altra nuova funzionalità di Vue 3.

La reattività è più espressiva in Vue 3

Nella prima parte di questo articolo, abbiamo toccato la motivazione standard per l'API di composizione, che è una migliore organizzazione e riutilizzo del codice. In effetti, il principale punto di forza della nuova API non è la sua potenza, ma la comodità organizzativa che porta: la capacità di strutturare il codice in modo più chiaro. Potrebbe sembrare tutto qui: l'API di composizione consente un modo per implementare i componenti che evita i limiti delle soluzioni già esistenti, come i mixin.

Tuttavia, c'è di più nella nuova API. L'API di composizione in realtà consente non solo sistemi reattivi meglio organizzati ma anche più potenti. L'ingrediente chiave è la capacità di aggiungere dinamicamente reattività all'applicazione. In precedenza, prima di caricare un componente era necessario definire tutti i dati, tutte le proprietà calcolate, ecc. Perché sarebbe utile aggiungere oggetti reattivi in ​​una fase successiva? In ciò che resta diamo un'occhiata a un esempio più complesso: i fogli di calcolo.

Creazione di un foglio di calcolo in Vue 2

Gli strumenti per fogli di calcolo come Microsoft Excel, LibreOffice Calc e Fogli Google hanno tutti una sorta di sistema di reattività. Questi strumenti presentano all'utente una tabella, con colonne indicizzate da A–Z, AA–ZZ, AAA–ZZZ, ecc. e righe indicizzate numericamente.

Ogni cella può contenere un valore semplice o una formula. Una cella con una formula è essenzialmente una proprietà calcolata, che può dipendere da valori o altre proprietà calcolate. Con i fogli di calcolo standard (ea differenza del sistema di reattività in Vue), queste proprietà calcolate possono persino dipendere da se stesse! Tale autoriferimento è utile in alcuni scenari in cui il valore desiderato è ottenuto mediante approssimazione iterativa.

Una volta modificato il contenuto di una cella, tutte le celle che dipendono dalla cella in questione attiveranno un aggiornamento. Se si verificano ulteriori modifiche, potrebbero essere programmati ulteriori aggiornamenti.

Se dovessimo creare un'applicazione per fogli di calcolo con Vue, sarebbe naturale chiedersi se possiamo utilizzare il sistema di reattività di Vue e rendere Vue il motore di un'app per fogli di calcolo. Per ogni cella, potremmo ricordare il suo valore modificabile grezzo, nonché il valore calcolato corrispondente. I valori calcolati rifletterebbero il valore grezzo se si tratta di un valore normale e, in caso contrario, i valori calcolati sono il risultato dell'espressione (formula) che viene scritta anziché un valore semplice.

Con Vue 2, un modo per implementare un foglio di calcolo consiste raw_values ​​un array bidimensionale di stringhe e computed_values ​​un array bidimensionale (calcolato) di valori di cella.

Se il numero di celle è piccolo e corretto prima del caricamento del componente Vue appropriato, potremmo avere un valore grezzo e un valore calcolato per ogni cella della tabella nella nostra definizione del componente. A parte la mostruosità estetica che una tale implementazione causerebbe, una tabella con un numero fisso di celle in fase di compilazione probabilmente non conta come foglio di calcolo.

Ci sono problemi anche con l'array bidimensionale computed_values ​​. Una proprietà calcolata è sempre una funzione la cui valutazione, in questo caso, dipende da se stessa (calcolare il valore di una cella, in generale, richiederà che siano già calcolati altri valori). Anche se Vue consentisse proprietà calcolate autoreferenziali, l'aggiornamento di una singola cella comporterebbe il ricalcolo di tutte le celle (indipendentemente dal fatto che ci siano dipendenze o meno). Questo sarebbe estremamente inefficiente. Pertanto, potremmo finire per utilizzare la reattività per rilevare le modifiche nei dati grezzi con Vue 2, ma tutto il resto in termini di reattività dovrebbe essere implementato da zero.

Modellazione di valori calcolati in Vue 3

Con Vue 3 possiamo introdurre una nuova proprietà calcolata per ogni cella. Se la tabella cresce, vengono introdotte nuove proprietà calcolate.

Supponiamo di avere le celle A1 e A2 e desideriamo che A2 visualizzi il quadrato di A1 il cui valore è il numero 5. Uno schizzo di questa situazione:

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

Supponiamo di rimanere in questo semplice scenario per un momento. C'è un problema qui; cosa succede se desideriamo cambiare A1 in modo che contenga il numero 6? Supponiamo di scrivere questo:

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

Questo non ha semplicemente cambiato il valore da 5 a 6 in A1 . La variabile A1 ha ora un'identità completamente diversa: la proprietà calcolata che si risolve nel numero 6. Tuttavia, la variabile A2 reagisce ancora ai cambiamenti della vecchia identità della variabile A1 . Quindi, A2 non dovrebbe riferirsi direttamente ad A1 , ma piuttosto a qualche oggetto speciale che sarà sempre disponibile nel contesto e ci dirà cos'è A1 al momento. In altre parole, abbiamo bisogno di un livello di indirizzamento prima di accedere ad A1 , qualcosa come un puntatore. Non ci sono puntatori come entità di prima classe in Javascript, ma è facile simularne uno. Se desideriamo avere un pointer che punta a un value , possiamo creare un pointer = {points_to: value} . Reindirizzare il puntatore equivale all'assegnazione a pointer.points_to e dereferenziare (accedere al valore puntato) equivale a recuperare il valore di pointer.points_to . Nel nostro caso procediamo come segue:

 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

Ora possiamo sostituire 5 con 6.

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

Sul server Discord di Vue, l'utente redblobgames ha suggerito un altro approccio interessante: invece di usare valori calcolati, usa riferimenti che avvolgono funzioni regolari. In questo modo, è possibile scambiare allo stesso modo la funzione senza modificare l'identità del riferimento stesso.

La nostra implementazione del foglio di calcolo avrà celle a cui si fa riferimento con chiavi di un array bidimensionale. Questo array può fornire il livello di indirizzamento richiesto. Pertanto, nel nostro caso, non sarà necessaria alcuna simulazione del puntatore aggiuntiva. Potremmo anche avere un array che non distingue tra valori grezzi e calcolati. Tutto può essere un valore calcolato:

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

Tuttavia, vogliamo davvero distinguere i valori grezzi da quelli calcolati poiché vogliamo essere in grado di associare il valore grezzo a un elemento di input HTML. Inoltre, se abbiamo un array separato per i valori grezzi, non dobbiamo mai modificare le definizioni delle proprietà calcolate; si aggiorneranno automaticamente in base ai dati grezzi.

Implementazione del foglio di calcolo

Cominciamo con alcune definizioni di base, che sono per la maggior parte autoesplicative.

 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);

Il piano prevede che ogni computed_values[row][column] venga calcolato come segue. Se raw_values[row][column] non inizia con = , restituisce raw_values[row][column] . In caso contrario, analizzare la formula, compilarla in JavaScript, valutare il codice compilato e restituire il valore. Per farla breve, bararemo un po' con le formule di analisi e non faremo alcune ottimizzazioni ovvie qui, come una cache di compilazione.

Assumiamo che gli utenti possano inserire qualsiasi espressione JavaScript valida come formula. Possiamo sostituire i riferimenti ai nomi di cella che appaiono nelle espressioni dell'utente, come A1, B5, ecc., con il riferimento al valore effettivo della cella (calcolato). La seguente funzione svolge questo lavoro, presupponendo che le stringhe che assomigliano ai nomi delle celle identifichino sempre le celle (e non facciano parte di un'espressione JavaScript non correlata). Per semplicità, assumeremo che gli indici di colonna siano costituiti da una singola lettera.

 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); };

Usando la funzione transpile , possiamo ottenere espressioni JavaScript pure da espressioni scritte nella nostra piccola "estensione" di JavaScript con riferimenti di cella.

Il passaggio successivo consiste nel generare proprietà calcolate per ogni cella. Questa procedura si verificherà una volta nella vita di ogni cella. Possiamo realizzare una fabbrica che restituirà le proprietà calcolate desiderate:

 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);

Se inseriamo tutto il codice sopra nel metodo di setup , dobbiamo restituire {raw_values, computed_values, rows, cols, letters, calculations} .

Di seguito, presentiamo il componente completo, insieme a un'interfaccia utente di base.

Il codice è disponibile su GitHub e puoi anche dare un'occhiata alla demo dal vivo.

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

Che dire dell'uso nel mondo reale?

Abbiamo visto come il sistema di reattività disaccoppiato di Vue 3 abilita non solo un codice più pulito, ma consente anche sistemi reattivi più complessi basati sul nuovo meccanismo di reattività di Vue. Sono passati circa sette anni dall'introduzione di Vue e l'aumento dell'espressività chiaramente non era molto ricercato.

L'esempio del foglio di calcolo è una semplice dimostrazione di ciò di cui Vue è capace ora e puoi anche dare un'occhiata alla demo dal vivo.

Ma come esempio di parole reali, è in qualche modo di nicchia. In che tipo di situazioni potrebbe tornare utile il nuovo sistema? Il caso d'uso più ovvio per la reattività su richiesta potrebbe essere il miglioramento delle prestazioni per applicazioni complesse.

Confronto tra funnel Vue 2 vs Vue 3.

Nelle applicazioni front-end che funzionano con una grande quantità di dati, il sovraccarico derivante dall'utilizzo di una reattività poco ponderata potrebbe avere un impatto negativo sulle prestazioni. Supponiamo di avere un'applicazione dashboard aziendale che produce report interattivi dell'attività aziendale dell'azienda. L'utente può selezionare un intervallo di tempo e aggiungere o rimuovere indicatori di prestazioni nel rapporto. Alcuni indicatori possono visualizzare valori che dipendono da altri indicatori.

Un modo per implementare la generazione di report è attraverso una struttura monolitica. Quando l'utente modifica un parametro di input nell'interfaccia, viene aggiornata una singola proprietà calcolata, ad esempio report_data . Il calcolo di questa proprietà calcolata avviene secondo un piano hardcoded: in primo luogo, calcolare tutti gli indicatori di performance indipendenti, poi quelli che dipendono solo da questi indicatori indipendenti, ecc.

Una migliore implementazione disaccoppierà i bit del report e li calcolerà in modo indipendente. Ci sono alcuni vantaggi in questo:

  • Lo sviluppatore non deve codificare un piano di esecuzione, che è noioso e soggetto a errori. Il sistema di reattività di Vue rileverà automaticamente le dipendenze.
  • A seconda della quantità di dati coinvolti, potremmo ottenere miglioramenti sostanziali delle prestazioni poiché stiamo solo aggiornando i dati del report che dipendevano logicamente dai parametri di input modificati.

Se tutti gli indicatori di performance che potrebbero far parte del report finale sono noti prima che il componente Vue venga caricato, potremmo essere in grado di implementare il disaccoppiamento proposto anche con Vue 2. Altrimenti, se il backend è l'unica fonte di verità (che è di solito il caso delle applicazioni basate sui dati) o se sono presenti fornitori di dati esterni, possiamo generare proprietà calcolate su richiesta per ogni parte di un report.

Grazie a Vue 3, ora questo non è solo possibile ma facile da fare.