Réactivité à la demande dans Vue 3

Publié: 2022-03-11

Outre des améliorations de performances admirables, la vue 3 récemment publiée a également apporté plusieurs nouvelles fonctionnalités. L'introduction la plus importante est sans doute l' API Composition . Dans la première partie de cet article, nous récapitulons la motivation standard pour une nouvelle API : une meilleure organisation et réutilisation du code. Dans la deuxième partie, nous nous concentrerons sur les aspects moins discutés de l'utilisation de la nouvelle API, tels que la mise en œuvre de fonctionnalités basées sur la réactivité qui étaient inexprimables dans le système de réactivité de Vue 2.

Nous appellerons cela la réactivité à la demande . Après avoir introduit les nouvelles fonctionnalités pertinentes, nous allons créer une application de feuille de calcul simple pour démontrer la nouvelle expressivité du système de réactivité de Vue. À la toute fin, nous discuterons de l'utilisation réelle que pourrait avoir cette amélioration de la réactivité à la demande.

Quoi de neuf dans Vue 3 et pourquoi c'est important

Vue 3 est une réécriture majeure de Vue 2, introduisant une pléthore d'améliorations tout en conservant la rétrocompatibilité avec l'ancienne API presque dans son intégralité.

L'une des nouvelles fonctionnalités les plus importantes de Vue 3 est l' API de composition . Son introduction a suscité beaucoup de controverse lors de sa première discussion publique. Si vous n'êtes pas déjà familiarisé avec la nouvelle API, nous décrirons d'abord la motivation qui la sous-tend.

L'unité habituelle d'organisation du code est un objet JavaScript dont les clés représentent différents types possibles d'un morceau d'un composant. Ainsi, l'objet peut avoir une section pour les données réactives ( data ), une autre section pour les propriétés calculées ( computed ), une autre pour les méthodes de composant ( methods ), etc.

Selon ce paradigme, un composant peut avoir plusieurs fonctionnalités non liées ou vaguement liées dont les rouages ​​internes sont répartis entre les sections de composants susmentionnées. Par exemple, nous pourrions avoir un composant pour télécharger des fichiers qui implémente deux fonctionnalités essentiellement distinctes : la gestion des fichiers et un système qui contrôle l'animation de l'état de téléchargement.

La partie <script> peut contenir quelque chose comme ce qui suit :

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

Il y a des avantages à cette approche traditionnelle de l'organisation du code, principalement dans le fait que le développeur n'a pas à se soucier de l'endroit où écrire un nouveau morceau de code. Si nous ajoutons une variable réactive, nous l'insérons dans la section des data . Si nous recherchons une variable existante, nous savons qu'elle doit se trouver dans la section des data .

Cette approche traditionnelle consistant à diviser l'implémentation de la fonctionnalité en sections ( data , computed , etc.) n'est pas adaptée à toutes les situations.

Les exceptions suivantes sont souvent citées :

  1. Traiter un composant avec un grand nombre de fonctionnalités. Si nous voulons mettre à jour notre code d'animation avec la possibilité de retarder le démarrage de l'animation, par exemple, nous devrons faire défiler/sauter entre toutes les sections pertinentes du composant dans un éditeur de code. Dans le cas de notre composant de téléchargement de fichiers, le composant lui-même est petit et le nombre de fonctionnalités qu'il implémente est également petit. Ainsi, dans ce cas, sauter entre les sections n'est pas vraiment un problème. Ce problème de fragmentation du code devient pertinent lorsque nous traitons de gros composants.
  2. Une autre situation où l'approche traditionnelle fait défaut est la réutilisation du code. Souvent, nous devons créer une combinaison spécifique de données réactives, de propriétés calculées, de méthodes, etc., disponibles dans plusieurs composants.

Vue 2 (et la version rétrocompatible Vue 3) offrent une solution à la plupart des problèmes d'organisation et de réutilisation du code : mixins .

Avantages et inconvénients des Mixins dans Vue 3

Les mixins permettent d'extraire les fonctionnalités d'un composant dans une unité de code distincte. Chaque fonctionnalité est placée dans un mixin séparé et chaque composant peut utiliser un ou plusieurs mixins. Les morceaux définis dans un mixin peuvent être utilisés dans un composant comme s'ils étaient définis dans le composant lui-même. Les mixins sont un peu comme les classes des langages orientés objet en ce sens qu'ils collectent le code lié à une fonctionnalité donnée. Comme les classes, les mixins peuvent être hérités (utilisés) dans d'autres unités de code.

Cependant, le raisonnement avec les mixins est plus difficile car, contrairement aux classes, les mixins n'ont pas besoin d'être conçus avec l'encapsulation à l'esprit. Les mixins sont autorisés à être des collections de morceaux de code vaguement liés sans interface bien définie avec le monde extérieur. L'utilisation de plusieurs mixins à la fois dans le même composant peut entraîner un composant difficile à comprendre et à utiliser.

La plupart des langages orientés objet (par exemple, C # et Java) découragent ou même interdisent l'héritage multiple malgré le fait que le paradigme de la programmation orientée objet dispose des outils nécessaires pour gérer une telle complexité. (Certains langages autorisent l'héritage multiple, comme C++, mais la composition est toujours préférée à l'héritage.)

Un problème plus pratique qui peut survenir lors de l'utilisation de mixins dans Vue est la collision de noms, qui se produit lors de l'utilisation de deux ou plusieurs mixins déclarant des noms communs. Il convient de noter ici que si la stratégie par défaut de Vue pour gérer les collisions de noms n'est pas idéale dans une situation donnée, la stratégie peut être ajustée par le développeur. Cela se fait au prix d'une plus grande complexité.

Un autre problème est que les mixins n'offrent pas quelque chose qui ressemble à un constructeur de classe. C'est un problème car nous avons souvent besoin de fonctionnalités très similaires, mais pas exactement identiques, pour être présentes dans différents composants. Cela peut être contourné dans certains cas simples grâce à l'utilisation d'usines de mixin.

Par conséquent, les mixins ne sont pas la solution idéale pour l'organisation et la réutilisation du code, et plus le projet est grand, plus leurs problèmes deviennent sérieux. Vue 3 introduit une nouvelle façon de résoudre les mêmes problèmes concernant l'organisation et la réutilisation du code.

API de composition : la réponse de Vue 3 à l'organisation et à la réutilisation du code

L'API de composition nous permet (mais ne nous oblige pas) de découpler complètement les éléments d'un composant. Chaque morceau de code (une variable, une propriété calculée, une montre, etc.) peut être défini indépendamment.

Par exemple, au lieu d'avoir un objet qui contient une section de data contenant une clé animation_state avec la valeur (par défaut) "playing", nous pouvons maintenant écrire (n'importe où dans notre code JavaScript) :

 const animation_state = ref('playing');

L'effet est presque le même que de déclarer cette variable dans la section de data d'un composant. La seule différence essentielle est que nous devons rendre la ref définie en dehors du composant disponible dans le composant où nous avons l'intention de l'utiliser. Pour ce faire, nous importons son module à l'endroit où le composant est défini et renvoyons la ref de la section de setup d'un composant. Nous allons ignorer cette procédure pour le moment et nous concentrer uniquement sur la nouvelle API pendant un moment. La réactivité dans Vue 3 ne nécessite aucun composant ; c'est en fait un système autonome.

Nous pouvons utiliser la variable animation_state dans n'importe quelle portée dans laquelle nous importons cette variable. Après avoir construit une ref , nous obtenons et définissons sa valeur réelle à l'aide ref.value , par exemple :

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

Nous avons besoin du suffixe '.value' car l'opérateur d'affectation affecterait autrement la valeur (non réactive) "paused" à la variable animation_state . La réactivité en JavaScript (à la fois lorsqu'elle est implémentée via le defineProperty comme dans Vue 2, et lorsqu'elle est basée sur un Proxy comme dans Vue 3) nécessite un objet dont les clés peuvent être utilisées de manière réactive.

Notez que c'était également le cas dans Vue 2 ; là, nous avions un composant comme préfixe pour tout membre de données réactif ( component.data_member ). À moins que et jusqu'à ce que la norme du langage JavaScript n'introduise la possibilité de surcharger l'opérateur d'affectation, les expressions réactives nécessiteront un objet et une clé (par exemple, animation_state et value comme ci-dessus) pour apparaître sur le côté gauche de toute opération d'affectation où nous souhaitons préserver la réactivité.

Dans les modèles, nous pouvons omettre .value car Vue doit prétraiter le code du modèle et peut détecter automatiquement les références :

 <animation :state='animation_state' />

En théorie, le compilateur Vue pourrait également prétraiter la partie <script> d'un composant de fichier unique (SFC) de la même manière, en insérant .value si nécessaire. Cependant, l'utilisation de refs serait alors différente selon que nous utilisons ou non des SFC, donc peut-être qu'une telle fonctionnalité n'est même pas souhaitable.

Parfois, nous avons une entité (par exemple, être un objet Javascript ou un tableau) que nous n'avons jamais l'intention de remplacer par une instance complètement différente. Au lieu de cela, nous pourrions uniquement être intéressés par la modification de ses champs à clé. Il y a un raccourci dans ce cas : utiliser reactive au lieu de ref nous permet de nous passer du .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 réactivité découplée avec ref et reactive n'est pas une fonctionnalité complètement nouvelle de Vue 3. Elle a été partiellement introduite dans Vue 2.6, où de telles instances découplées de données réactives étaient appelées « observables ». Pour la plupart, on peut remplacer Vue.observable par reactive . L'une des différences est que l'accès et la mutation de l'objet transmis directement à Vue.observable est réactif, tandis que la nouvelle API renvoie un objet proxy, de sorte que la mutation de l'objet d'origine n'aura pas d'effet réactif.

Comparaison : API d'options et API de composition.

Ce qui est complètement nouveau dans Vue 3, c'est que d'autres éléments réactifs d'un composant peuvent désormais également être définis indépendamment, en plus des données réactives. Les propriétés calculées sont implémentées de la manière attendue :

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

De même, on peut implémenter différents types de montres, méthodes de cycle de vie et injection de dépendances. Par souci de brièveté, nous ne les couvrirons pas ici.

Supposons que nous utilisions l'approche SFC standard pour le développement de Vue. Nous pourrions même utiliser l'API traditionnelle, avec des sections séparées pour les données, les propriétés calculées, etc. Comment intégrons-nous les petits éléments de réactivité de l'API Composition avec les SFC ? Vue 3 introduit une autre section juste pour cela : setup . La nouvelle section peut être considérée comme une nouvelle méthode de cycle de vie (qui s'exécute avant tout autre crochet, en particulier avant la created ).

Voici un exemple de composant complet qui intègre l'approche traditionnelle avec l'API 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>

Ce qu'il faut retenir de cet exemple :

  • Tout le code de l'API Composition est maintenant dans setup . Vous souhaiterez peut-être créer un fichier distinct pour chaque fonctionnalité, importer ce fichier dans un SFC et renvoyer les bits de réactivité souhaités à partir de setup (pour les rendre disponibles pour le reste du composant).
  • Vous pouvez mélanger l'approche nouvelle et traditionnelle dans le même fichier. Notez que x , même s'il s'agit d'une référence, ne nécessite pas .value lorsqu'il est référencé dans le code du modèle ou dans les sections traditionnelles d'un composant tel que computed .
  • Enfin, notez que nous avons deux nœuds DOM racine dans notre modèle ; la possibilité d'avoir plusieurs nœuds racine est une autre nouvelle fonctionnalité de Vue 3.

La réactivité est plus expressive dans Vue 3

Dans la première partie de cet article, nous avons abordé la motivation standard de l'API de composition, qui est l'amélioration de l'organisation et de la réutilisation du code. En effet, le principal argument de vente de la nouvelle API n'est pas sa puissance, mais la commodité organisationnelle qu'elle apporte : la possibilité de structurer plus clairement le code. Il peut sembler que c'est tout, que l'API Composition permet d'implémenter des composants qui évitent les limitations des solutions déjà existantes, telles que les mixins.

Cependant, il y a plus dans la nouvelle API. L'API de composition permet en fait non seulement des systèmes réactifs mieux organisés mais plus puissants. L'ingrédient clé est la possibilité d'ajouter dynamiquement de la réactivité à l'application. Auparavant, il fallait définir toutes les données, toutes les propriétés calculées, etc. avant de charger un composant. Pourquoi ajouter des objets réactifs ultérieurement serait-il utile ? Dans ce qui reste, nous examinons un exemple plus complexe : les feuilles de calcul.

Créer une feuille de calcul dans Vue 2

Les outils de tableur tels que Microsoft Excel, LibreOffice Calc et Google Sheets ont tous une sorte de système de réactivité. Ces outils présentent à un utilisateur un tableau, avec des colonnes indexées par A–Z, AA–ZZ, AAA–ZZZ, etc., et des lignes indexées numériquement.

Chaque cellule peut contenir une valeur simple ou une formule. Une cellule avec une formule est essentiellement une propriété calculée, qui peut dépendre de valeurs ou d'autres propriétés calculées. Avec les feuilles de calcul standard (et contrairement au système de réactivité de Vue), ces propriétés calculées peuvent même dépendre d'elles-mêmes ! Une telle auto-référence est utile dans certains scénarios où la valeur souhaitée est obtenue par approximation itérative.

Une fois que le contenu d'une cellule change, toutes les cellules qui dépendent de la cellule en question déclencheront une mise à jour. Si d'autres modifications se produisent, d'autres mises à jour peuvent être programmées.

Si nous devions créer une application de feuille de calcul avec Vue, il serait naturel de se demander si nous pouvons utiliser le propre système de réactivité de Vue et faire de Vue le moteur d'une application de feuille de calcul. Pour chaque cellule, nous pourrions retenir sa valeur brute éditable, ainsi que la valeur calculée correspondante. Les valeurs calculées reflètent la valeur brute s'il s'agit d'une valeur simple, et sinon, les valeurs calculées sont le résultat de l'expression (formule) écrite à la place d'une valeur simple.

Avec Vue 2, une façon d'implémenter une feuille de calcul est d'avoir raw_values ​​un tableau de chaînes à deux dimensions, et computed_values ​​un tableau à deux dimensions (calculé) de valeurs de cellules.

Si le nombre de cellules est petit et fixe avant le chargement du composant Vue approprié, nous pourrions avoir une valeur brute et une valeur calculée pour chaque cellule du tableau dans notre définition de composant. Mis à part la monstruosité esthétique qu'une telle implémentation entraînerait, un tableau avec un nombre fixe de cellules au moment de la compilation ne compte probablement pas comme une feuille de calcul.

Il y a aussi des problèmes avec le tableau à deux dimensions computed_values ​​. Une propriété calculée est toujours une fonction dont l'évaluation, dans ce cas, dépend d'elle-même (le calcul de la valeur d'une cellule nécessitera, en général, que d'autres valeurs soient déjà calculées). Même si Vue autorisait les propriétés calculées auto-référentielles, la mise à jour d'une seule cellule entraînerait le recalcul de toutes les cellules (qu'il existe ou non des dépendances). Ce serait extrêmement inefficace. Ainsi, nous pourrions finir par utiliser la réactivité pour détecter les changements dans les données brutes avec Vue 2, mais tout le reste en termes de réactivité devrait être implémenté à partir de zéro.

Modélisation des valeurs calculées dans Vue 3

Avec Vue 3, nous pouvons introduire une nouvelle propriété calculée pour chaque cellule. Si la table s'agrandit, de nouvelles propriétés calculées sont introduites.

Supposons que nous ayons les cellules A1 et A2 , et que nous souhaitions que A2 affiche le carré de A1 dont la valeur est le nombre 5. Un schéma de cette situation :

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

Supposons que nous restions un instant dans ce scénario simple. Il y a un problème ici; que se passe-t-il si nous souhaitons modifier A1 pour qu'il contienne le chiffre 6 ? Supposons que nous écrivions ceci :

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

Cela n'a pas simplement changé la valeur 5 en 6 dans A1 . La variable A1 a maintenant une identité complètement différente : la propriété calculée qui se résout au nombre 6. Cependant, la variable A2 réagit toujours aux changements de l'ancienne identité de la variable A1 . Ainsi, A2 ne devrait pas faire directement référence à A1 , mais plutôt à un objet spécial qui sera toujours disponible dans le contexte et qui nous dira ce qu'est A1 à ce moment. En d'autres termes, nous avons besoin d'un niveau d'indirection avant d'accéder à A1 , quelque chose comme un pointeur. Il n'y a pas de pointeurs comme entités de première classe en Javascript, mais il est facile d'en simuler un. Si nous souhaitons avoir un pointer pointant vers une value , nous pouvons créer un objet pointer = {points_to: value} . Rediriger le pointeur revient à assigner à pointer.points_to , et déréférencer (accéder à la valeur pointée) revient à récupérer la valeur de pointer.points_to . Dans notre cas nous procédons comme suit :

 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

Maintenant, nous pouvons remplacer 5 par 6.

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

Sur le serveur Discord de Vue, l'utilisateur redblobgames a suggéré une autre approche intéressante : au lieu d'utiliser des valeurs calculées, utilisez des références qui enveloppent des fonctions régulières. De cette façon, on peut également échanger la fonction sans changer l'identité de la référence elle-même.

Notre implémentation de feuille de calcul aura des cellules référencées par des clés d'un tableau à deux dimensions. Ce tableau peut fournir le niveau d'indirection dont nous avons besoin. Ainsi, dans notre cas, nous n'aurons besoin d'aucune simulation de pointeur supplémentaire. Nous pourrions même avoir un tableau qui ne fait pas la distinction entre les valeurs brutes et calculées. Tout peut être une valeur calculée :

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

Cependant, nous voulons vraiment distinguer les valeurs brutes et calculées car nous voulons pouvoir lier la valeur brute à un élément d'entrée HTML. De plus, si nous avons un tableau séparé pour les valeurs brutes, nous n'avons jamais à modifier les définitions des propriétés calculées ; ils seront mis à jour automatiquement en fonction des données brutes.

Implémentation de la feuille de calcul

Commençons par quelques définitions de base, qui sont pour la plupart explicites.

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

Le plan est que chaque computed_values[row][column] soit calculé comme suit. Si raw_values[row][column] ne commence pas par = , retourne raw_values[row][column] . Sinon, analysez la formule, compilez-la en JavaScript, évaluez le code compilé et renvoyez la valeur. Pour faire court, nous tricherons un peu avec les formules d'analyse et nous ne ferons pas d'optimisations évidentes ici, comme un cache de compilation.

Nous supposerons que les utilisateurs peuvent saisir n'importe quelle expression JavaScript valide sous forme de formule. Nous pouvons remplacer les références aux noms de cellule qui apparaissent dans les expressions de l'utilisateur, telles que A1, B5, etc., par la référence à la valeur réelle de la cellule (calculée). La fonction suivante effectue ce travail, en supposant que les chaînes ressemblant à des noms de cellules identifient toujours les cellules (et ne font pas partie d'une expression JavaScript non liée). Pour plus de simplicité, nous supposerons que les indices de colonne se composent d'une seule lettre.

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

En utilisant la fonction transpile , nous pouvons obtenir des expressions JavaScript pures à partir d'expressions écrites dans notre petite "extension" de JavaScript avec des références de cellule.

L'étape suivante consiste à générer des propriétés calculées pour chaque cellule. Cette procédure se produira une fois dans la durée de vie de chaque cellule. Nous pouvons créer une usine qui renverra les propriétés calculées souhaitées :

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

Si nous mettons tout le code ci-dessus dans la méthode setup , nous devons retourner {raw_values, computed_values, rows, cols, letters, calculations} .

Ci-dessous, nous présentons le composant complet, ainsi qu'une interface utilisateur de base.

Le code est disponible sur GitHub, et vous pouvez également consulter la démo en direct.

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

Qu'en est-il de l'utilisation dans le monde réel ?

Nous avons vu comment le système de réactivité découplé de Vue 3 permet non seulement un code plus propre, mais aussi des systèmes réactifs plus complexes basés sur le nouveau mécanisme de réactivité de Vue. Environ sept ans se sont écoulés depuis l'introduction de Vue, et l'augmentation de l'expressivité n'était clairement pas très recherchée.

L'exemple de feuille de calcul est une démonstration simple de ce dont Vue est capable maintenant, et vous pouvez également consulter la démo en direct.

Mais en tant qu'exemple concret, c'est un peu une niche. Dans quelles situations le nouveau système pourrait-il être utile ? Le cas d'utilisation le plus évident de la réactivité à la demande pourrait être les gains de performances pour les applications complexes.

Comparaison de l'entonnoir Vue 2 vs Vue 3.

Dans les applications frontales qui fonctionnent avec une grande quantité de données, la surcharge liée à l'utilisation d'une réactivité mal pensée peut avoir un impact négatif sur les performances. Supposons que nous ayons une application de tableau de bord d'entreprise qui produit des rapports interactifs sur l'activité commerciale de l'entreprise. L'utilisateur peut sélectionner une plage de temps et ajouter ou supprimer des indicateurs de performance dans le rapport. Certains indicateurs peuvent afficher des valeurs qui dépendent d'autres indicateurs.

Une façon de mettre en œuvre la génération de rapports consiste à utiliser une structure monolithique. Lorsque l'utilisateur modifie un paramètre d'entrée dans l'interface, une seule propriété calculée, par exemple, report_data , est mise à jour. Le calcul de cette propriété calculée se fait selon un plan codé en dur : d'abord, calculez tous les indicateurs de performance indépendants, puis ceux qui ne dépendent que de ces indicateurs indépendants, etc.

Une meilleure implémentation découplera les bits du rapport et les calculera indépendamment. Il y a quelques avantages à cela :

  • Le développeur n'a pas à coder en dur un plan d'exécution, ce qui est fastidieux et sujet aux erreurs. Le système de réactivité de Vue détectera automatiquement les dépendances.
  • Selon la quantité de données impliquées, nous pourrions obtenir des gains de performances substantiels puisque nous ne mettons à jour que les données du rapport qui dépendaient logiquement des paramètres d'entrée modifiés.

Si tous les indicateurs de performance pouvant faire partie du rapport final sont connus avant le chargement du composant Vue, nous pourrons peut-être mettre en œuvre le découplage proposé même avec Vue 2. Sinon, si le backend est la seule source de vérité (qui est généralement le cas avec les applications pilotées par les données), ou s'il existe des fournisseurs de données externes, nous pouvons générer des propriétés calculées à la demande pour chaque élément d'un rapport.

Grâce à Vue 3, c'est maintenant non seulement possible mais facile à faire.