Vue: Attendre les hooks de cycle de vie des composants asynchrones

Créé le 8 déc. 2017  ·  51Commentaires  ·  Source: vuejs/vue

Quel problème cette fonctionnalité résout-elle ?

Si un utilisateur doit implémenter un hook de cycle de vie qui dépend d'opérations asynchrones, vue doit respecter la nature asynchrone du hook implémenté et l'attendre dans vue land.

A quoi ressemble l'API proposée ?

L'API ne change pas ; seulement comment cela fonctionne maintenant en attendant les crochets asynchrones.

Commentaire le plus utile

C'est le code réel que je veux être attendu:

  beforeMount: async function() {
       this.user = await client.get({type: 'user', id: this.$route.params.id});
    }

Ce qui ferait partie du composant UserPage .

Tous les 51 commentaires

Alors tu veux

created () {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log('created')
            resolve()
        })
    })
},
mounted () {
    console.log('mounted')
}

afficher

mounted

?

Lors de la création d'une demande de fonctionnalité, veuillez ajouter un cas d'utilisation réel pour que la demande mérite d'être mise en œuvre.

Bien que ce soit théoriquement une bonne idée, cela nécessite une refonte/réécriture fondamentale de l'architecture à réaliser, et peut potentiellement casser beaucoup de logique qui repose sur la nature synchrone des crochets de cycle de vie. Les avantages doivent donc être substantiels pour justifier ce changement - sinon, cela ne peut être envisagé que lorsque nous prévoyons de faire une mise à niveau complète, ce qui est peu susceptible de se produire très bientôt.

Clôture pour le moment, mais n'hésitez pas à poursuivre avec des raisonnements / cas d'utilisation / implications plus concrets.

@posva Compris -- Je m'excuse. Mon cas d'utilisation actuel est celui où j'ai un composant UserPage qui reçoit un user_id des paramètres d'itinéraire de la page (via this.$route.params ), puis récupère les données utilisateur réelles de la base de données sur le serveur en utilisant une commande comme :
this.user = await client.get({type: 'user', id: this.$route.params.id})
this.user fait référence à un champ user dans la partie data composant UserPage .

Idéalement, je veux que cette ligne de code soit exécutée après la création du composant (afin que this.$route.params soit disponible) mais avant que le composant ne soit réellement monté afin que dans mon modèle, je puisse utiliser en toute sécurité user interpolations sans obtenir d'erreurs sur les valeurs non définies.

@yyx990803
Je suis peut-être un novice ici, mais le seul changement ne devrait-il pas être l'ajout du mot-clé await avant l'appel pour les hooks de cycle de vie comme mounted , etc, dans Vue land ?

C'est le code réel que je veux être attendu:

  beforeMount: async function() {
       this.user = await client.get({type: 'user', id: this.$route.params.id});
    }

Ce qui ferait partie du composant UserPage .

Pas de soucis! J'imaginais ce cas d'utilisation. Il est préférable de le gérer comme décrit dans la documentation de vue-router car il ouvre différentes manières d'afficher l'état de chargement. Vous pouvez déjà attendre que les données soient là avant de rendre le composant d'ailleurs.

D'accord c'est logique. Maintenant, cependant, que se passe-t-il si j'ai un composant utilisateur qui est une version allégée de la page utilisateur (par exemple, comme le composant qui apparaît lorsque vous survolez le nom d'un utilisateur sur Facebook et "jetez un coup d'œil" dans son profil), alors le routeur n'est pas impliqué ici et le id sera passé en tant que propriété au composant.

En prenant ici une vue d'ensemble, les fonctions de JavaScript peuvent désormais être synchrones ou asynchrones, et ces crochets de cycle de vie étant des fonctions, et la façon dont nous les considérons comme des fonctions, devraient prendre en charge l'asynchrone (comme le démontre mon cas d'utilisation et ma "portée" intuitive pour l'approche que j'utilise ici).

Vous avez plusieurs façons de le faire. Le plus simple consiste à utiliser une variable qui commence par null, à récupérer les données et à les définir, en basculant le composant réel (à cause d'un v-if). Une version plus exotique serait une fonction qui résout le composant et utilise un <component :is="myDynamicComp"/> 😄
Mais s'il vous plaît, ne détournez pas le problème dans une question utilisez le forum ou discord pour cela

Non, je ne veux vraiment pas d'aide avec le code ! En fait, j'ai déjà mis en place des solutions de contournement très similaires à vos suggestions. Ce que j'essaie de dire, c'est qu'il est beaucoup plus intuitif d'utiliser simplement les fonctionnalités asynchrones de JS.

La partie que je n'avais pas réalisée est que le code asynchrone et le code synchrone sont de nature fondamentalement différente, de sorte que le code synchrone ne peut pas être forcé d'adhérer au code asynchrone sans se transformer fondamentalement en code asynchrone. yyx990803 l'a vu immédiatement mais il m'a fallu un certain temps pour comprendre complètement son commentaire. Merci pour votre temps les gars et désolé s'il y a eu un problème de communication de ma part quelque part en cours de route.

J'ai un cas d'utilisation ici et j'aimerais obtenir des suggestions et une méthode de contournement.

MainPage.vue est mon conteneur principal. J'appelle ajax "/init" à beforeCreate pour obtenir des informations sur l'utilisateur, puis je m'engage sur Vuex.store.
Content.vue est l'enfant à l'intérieur de MainPage.vue . Je voudrais appeler différents appels d'API au stade mounted fonction du rôle de l'utilisateur provenant de Vuex.store.

Si l'appel de cycle de vie dans le flux async/attente, il suivrait l'ordre
Parent beforeCreate -> Parent create -> Child beforeCreate -> Child create -> Child monté .... (Si je comprends bien le cycle de vie des composants).

Mais actuellement, je ne peux pas obtenir d'informations sur l'utilisateur à Content.vue , comment puis-je trouver une solution de contournement maintenant ?
Je voudrais garder l'API "/init" appelée à l'intérieur de MainPage.vue car elle est utilisée dans de nombreuses pages (conteneur dans Vue-Router).

question postée sur stackoverflow

Merci

une solution de contournement hackish pour ce qui vaut:

{
  created: function(){
    this.waitData = asyncCall();
  },
  mounted: function(){
    this.waitData.then(function(data) { ... })
  }
}


Une solution possible plus « plate » :

{
    async created () {
        let createdResolve = null
        let createdReject = null
        this.createdPromise = new Promise(function(resolve, reject){
            createdResolve = resolve
            createdReject = reject
        })
        await asyncCall1()
        await asyncCall2()
        ...
        createdResolve(someResult)
    }
    async mounted () {
        let result = await this.createdPromise
        ...
    }
    data () {
        return {
            createdPromise: null
        }
    }
}

N'est-ce pas encore une chose ?

data() {
 ...
},
async created() {
  const something = await exampleMethod();
  console.log(something);
}

Travaille pour moi (comme le mentionne @fifman ).

@breaddams Oui, bien sûr. Les fonctions _à l'intérieur_ de la méthode created seront attendues - mais la fonction created ou mounted elle-même ne l'est pas.

Ainsi, l'instance Vue appellera created et instantanément mounted avant que l'un des longs processus de created ne soit terminé

Ah , mon mauvais @darren-dev - cas d'utilisation différent de mon côté, mais je vois le problème maintenant merci pour la clarification.

@breaddams Pas de problème du tout - nous sommes tous là pour nos propres cas, heureux d'avoir pu clarifier!

Le hook de cycle de vie asynchrone peut être une bonne implémentation dans la prochaine version majeure

Il me semble qu'autoriser la prise en charge asynchrone des méthodes de cycle de vie encouragera par défaut les mauvaises pratiques UX. Comment? Les fonctions asynchrones sont utilisées pour les demandes qui ne peuvent pas être exécutées immédiatement (par exemple, les demandes de longue durée ou de réseau). Forcer Vue à retarder la création ou le montage ou l'une des autres méthodes de cycle de vie à attendre votre demande de réseau ou un processus asynchrone de longue durée aura un impact notable sur l'utilisateur. Imaginez un utilisateur venant sur votre site et devant attendre 4 secondes avec un écran vide pendant que le composant attend que la connexion cellulaire irrégulière de l'utilisateur termine votre demande de réseau. Et non seulement cela a un impact négatif sur l'utilisateur, mais vous sacrifiez également votre contrôle sur la situation - en tant que développeur, vous ne pouvez rien faire pour que les utilisateurs perçoivent le temps de chargement plus rapidement ou pour afficher des indicateurs de progression déterminés ou indéterminés. Donc, en intégrant cette capacité dans Vue, vous ne faites pas du Web un meilleur endroit ; vous activez les mauvaises pratiques.

Bien mieux pour planifier et concevoir le cas asynchrone dès le départ : lancez votre processus asynchrone dans created ou mounted ou n'importe où, puis faites en sorte que votre composant ait une structure squelette ou au pire un spinner pendant que vous attendez que votre API renvoie les autorisations de l'utilisateur. Bien meilleure UX et vous ne sacrifiez aucun contrôle. Et Vue n'a pas besoin d'ajouter de code pour gérer les fonctions de cycle de vie asynchrones, en gardant le paquet plus petit. Gagner gagner.

@seanfisher Vous soulevez un point valable. D'un point de vue architectural, la conception autour d'un ensemble d'événements asynchrones doit être gérée par le développeur - car c'est la seule façon de présenter correctement le message.

Avis de non -


Cependant, dicter les modèles de conception d'un développeur ne doit pas être laissé au framework que vous utilisez. Mon argument est que si vous n'attendez pas la fin d'une phase, alors pourquoi avoir des phases différentes ? Pourquoi avoir une scène créée, puis montée ? Si tout se passe fondamentalement en même temps, alors ignorer complètement l'étape créée est acceptable.

Littéralement, la seule fois où j'ai jamais créé (depuis le début de Vue) c'est lorsque je devais injecter quelque chose sur lequel vue devait s'appuyer - cela n'avait rien à voir avec la configuration ou la mise en page de ma page. Cependant, j'ai dû attendre que des tâches asynchrones (courtes) s'exécutent, ce qui serait bien meilleur _avant_ que la page ne soit rendue (comme la connexion aux méthodes d'authentification de Firebase). Avoir cela dans la création, puis attendre qu'il se termine avant de le monter réduirait complètement le besoin de solutions de contournement piratées.

Rappelez-vous, mon argument est que Vue ne devrait pas me dire que je développe mal. Il devrait simplement fournir la fonctionnalité souhaitée.

Cependant, dicter les modèles de conception d'un développeur ne doit pas être laissé au framework que vous utilisez.

Euh... Les frameworks sont conçus pour limiter ou guider ou "cadrer" spécifiquement le développeur dans certains modèles et pratiques de conception. C'est leur objectif principal. Tout bon framework proposera une API intelligente, qui offre justement une manière claire et évidente de travailler avec le framework et pourtant elle sera également contraignante.

Oui, il est paradoxal qu'un framework offre certaines capacités, mais contraint également le développeur en même temps à certaines pratiques de conception. C'est exactement là que l'opinion au sein des Frameworks peut l'aider ou la blesser. Il est difficile de trouver le bon équilibre et je pense que Vue ou plutôt Evan et l'équipe de développement de Vue ont fait et font un excellent travail en portant ces jugements.

Scott

Je ne soutiendrai jamais qu'un framework bien conçu doit être étendu avec le même modèle de conception, mais mon argument est que le framework ne peut pas le dicter. Vous avez raison, mais je dis que quelle que soit la qualité du framework, le développeur final doit toujours être prêt à faire ce qu'il veut.

Mais vous n'avez pas abordé l'argument _réel_ consistant à rendre les événements créés et montés asynchrones - vous venez d'ajouter votre avis (ce qui n'est pas faux) sur mon avis, ce qui conduit généralement à un énorme déraillement.

Je ne soutiendrai jamais qu'un framework bien conçu doit être étendu avec le même modèle de conception, mais mon argument est que le framework ne peut pas le dicter.

Désolé, mais cela n'a aucun sens pour moi. S'il vous plaît, montrez-moi un cadre qui ne dicte pas comment il doit être étendu.

Je pensais que mon dicton « Evan et sa compagnie font preuve de bon sens » montrerait mon opinion. Mais, pour être plus clair. Les hooks de cycle de vie montés et créés n'ont pas besoin de fonctionner de manière asynchrone ou plutôt, le fait qu'ils fonctionnent de manière synchrone aide à raisonner sur la logique de l'application. Toute "attente" doit être prise en compte dans l'interface utilisateur de toute façon et le code asynchrone peut toujours être exécuté dans chaque hook. Nous pouvons discuter du fait que les crochets beforeMount et mount sont maintenant nécessaires. Mais, il peut y avoir une ou deux choses dont vous pourriez avoir besoin dans mount, par exemple, auxquelles vous ne pouvez pas encore accéder dans created, comme la fonction de rendu compilée.

Scott

Si Vue a une opinion dans un sens ou dans l'autre sur les hooks de cycle de vie asynchrones, cela ne devrait pas être une question de spéculation. Il n'est pas nécessaire de spéculer sur le moment où les normes, API, guides et meilleures pratiques que Vue adopte, fournit ou recommande sont accessibles à tous.

Dans la réponse originale d'Evan, les hooks de cycle de vie asynchrones ne sont pas dans l'API standard, non pas parce que c'est nécessairement une mauvaise idée, mais parce que les avantages ne sont pas assez substantiels pour justifier le coût de la mise en œuvre.

Pour l'opinion que c'est une mauvaise pratique de faire attendre l'utilisateur pour un created asynchrone ou un autre crochet de cycle de vie sans indicateur que quelque chose se passe, l'argument peut être avancé que Vue, ayant pris en charge les crochets asynchrones, maintenant peut-être fournit quelque chose qui pourrait être appelé "modèles de phase" qui résoudrait également le problème (et qui pourrait être facile à mettre en œuvre).

Pour l'opinion que c'est une mauvaise pratique de faire attendre l'utilisateur pour un crochet asynchrone créé ou autre cycle de vie sans indicateur que quelque chose se passe,

Est-ce vraiment même un problème ?

Scott

Voici le problème que j'ai - et peut-être que quelqu'un peut suggérer comment JE DEVRAIS procéder car cela semble problématique.

Notre application Vue (plutôt grande) utilise largement Vuex. Dans bon nombre de nos composants Vue du cycle de vie create(), nous définissons via store.dispatch() certains éléments du magasin (évidemment). Cependant, comme cela a été porté à mon attention, store.dispatch() renvoie TOUJOURS une promesse .. même si la logique et la fonction sous-jacentes ne sont PAS asynchrone. J'ai donc mis async created() { wait store.dispatch('foo/action') } mais comme indiqué, cela échoue en fait ..

J'utilise également Typescript et il se plaint assez amèrement quand je n'attends pas / .puis les appels store.dispatch() .. ayant des promesses "flottantes" ..

Alors, quelle est la meilleure façon d'utiliser Vuex store.dispatch() dans un cycle de vie lorsque nous ne pouvons pas les asynchroniser ?

À votre santé!!

Toutes les autres discussions sur les opinions spécifiques de vue, et si les frameworks devraient imposer des opinions de côté, il pourrait être bénéfique de documenter ce comportement plus clairement.

Je regarde la solution "plus plate" de mounted() revient. Dans cette solution, created() et mounted() sont asynchrones, donc Vue appellera chacun d'eux et ils reviendront plus ou moins immédiatement, avec les choses asynchrones en arrière-plan. En fait, je ne sais pas en quoi c'est mieux que de simplement supprimer le createdPromise et de faire tout le travail asynchrone dans created() ou mounted() , comme :

async mounted() {
  let response = await fetch(my_url)
  let data = await response.text()
  my_data_member = data
}

Dans tous les cas, my_data_member sera rempli "plus tard" lorsque le XHR se terminera, longtemps après que mounted() renvoyé sa promesse, n'est-ce pas ?

Vous pouvez également essayer d'utiliser v-if sur le composant racine <div id="app"/> et le déclencher lorsque le chargement de vos données est terminé. Par exemple, je l'utilise pour le jeton de rafraîchissement.

C'est une fonctionnalité intéressante à avoir et à avoir beaucoup de cas d'utilisation. Pour mon cas d'utilisation, je dois charger des propriétés d'API spécifiques avant le rendu réel du composant. Cependant, je ne pense pas que l'async dans les crochets de cycle de vie soit une solution idéale.

Actuellement, tout le monde mentionne ici un scénario parfait où les fonctions asynchrones fonctionnent correctement. Mais disons, par exemple, si la connexion des utilisateurs est lente ou en retard et que nous attendons le montage du composant après la réponse d'une API, alors tout le cycle de vie du composant est distribué. Nous ne pourrons pas montrer les informations de chargement à l'utilisateur. Ou pire encore, si l'API expire ou renvoie une erreur, l'application se bloquera sans être montée.

Bien que ce soit une fonctionnalité intéressante, je suggérerais qu'elle soit gérée par l'application plutôt que par le framework, car l'implémentation de la logique de domaine a plus d'informations sur la logique sous-jacente que le framework, créant ainsi moins d'effets secondaires.

+1 pour cette fonctionnalité.
J'ai un mixin utilisé par divers composants, le mixin récupère les données d'une base de données dans le crochet monté.
Les composants doivent fonctionner avec les données récupérées par le mixin également dans le crochet monté.
Dans l'état actuel des choses, j'ai dû implémenter un rappel à appeler lorsque le mixin a fini de charger les données et d'abandonner le crochet monté dans le composant.
Cela fonctionne mais serait plus joli si le crochet monté gérait les promesses.

@cederron purement par intérêt, pourquoi ne pouvez-vous pas télécharger les données dans le composant parent et les transmettre en tant qu'accessoire ? utilisez un v-if pour afficher un indicateur de chargement pendant le chargement des données et lorsque les données s'affichent, vous pouvez afficher le composant, et lorsqu'il sera créé, il disposera de toutes les données dont il a besoin.

Évite que le composant représente deux états distincts non liés (« aucune donnée chargée, en attente » et « les données ont été chargées, vous pouvez les manipuler »)

Si vous devez réutiliser la logique à plusieurs endroits, voire déplacer la logique de téléchargement vers Vuex, cela a du sens car le téléchargement des données se ferait dans une action Vuex plutôt que dans un composant.

@ReinisV Je pense que j'ai trop simplifié mon cas, le composant crée de nouvelles données à partir des données récupérées par le crochet monté par mixin, et la vue du composant est liée à ces nouvelles données.
Ainsi, le mixin doit récupérer les données de la base de données > le composant crée des données à partir de celle-ci > maintenant le composant est affiché

AFAIK cela ne fonctionnera pas:

export const MyMixin = {
    data: function () {
        return {
            dbData: null
        }
    },
   mounted:  async function () {
      this.dbData = await asyncFetchDataFromDB()
   }
}


export const MyComponent = {
    mixins: [MyMixin],
    data: function () {
        return {
            newData: null
        }
    },
   mounted:  function () {
      this.newData = handleDBData(this.dbData)
   }
}

dbData sera nul dans le hook de montage du composant.

Actuellement, j'exécute un rappel lorsque le mixin récupère les données, mais il serait plus joli de simplement rendre la fonction de montage asynchrone.

Je ne peux pas en dire beaucoup sur vuex car je ne l'utilise pas

Je veux vraiment réitérer ce que @seanfisher a mentionné ici. Le fait d'avoir vue attendre sur des composants marqués comme async pose non seulement des problèmes pour les utilisateurs, mais le modèle d'utilisation d'async/attente pour lancer la recherche de données est présent partout. Cela forcerait la conversion explicite du code de ces hooks de cycle de vie en promesses non attendues pour éviter explicitement de bloquer vue. Dans certains cas, cela pourrait être bon, et si une fonctionnalité était introduite, je devrais suggérer d'exécuter deux cycles de vie en même temps, l'actuel qui gère l'exécution des hooks de composant et l'autre qui attend ces exécutions pour les rappels globaux.

Mais je serais vraiment déçu de réécrire littéralement chacun de mes crochets de cycle de vie pour éviter de bloquer la vue, async/await est beaucoup plus propre, donc nous n'utilisons pas de promesses. Changer cela d'une manière non rétrocompatible change le puits de réussite (cycle de vie des composants non attendu) pour un puits d'échec.

Bien mieux pour planifier et concevoir le cas asynchrone dès le départ : lancez votre processus asynchrone dans created ou mounted ou n'importe où, puis faites en sorte que votre composant ait une structure squelette ou au pire un spinner pendant que vous attendez que votre API renvoie les autorisations de l'utilisateur. Bien meilleure UX et vous ne sacrifiez aucun contrôle. Et Vue n'a pas besoin d'ajouter de code pour gérer les fonctions de cycle de vie asynchrones, en gardant le paquet plus petit. Gagner gagner.

@seanfisher Merci, c'est super utile !

Pourquoi ne pas utiliser à la place un composant asynchrone, qui ne sera rendu que lorsque les appels asynchrones seront retournés ?
plus d'infos sur l'API ici
https://vuejs.org/v2/guide/components-dynamic-async.html#Async -Composants

new Vue({
  components: {
    root: () => ({ // Aync component
      // The component to load (should be a Promise)
      component: new Promise(async function (resolve) {
        await FetchMyVariables()
        resolve(MyComponent) // MyComponent will be rendered only when FetchMyVariables has returned
      }),
      // A component to use while the async component is loading
      loading: { render: (h) => h('div', 'loading') }, 
    })
  },
  render: h => h('root')
})

Bien que la plupart de ces solutions semblent bien, je pense que c'est l'une de ces pièces manquantes majeures de Vue qui le rendent si intuitif. Je pense que Vue 3 doit implémenter cela car nous sommes arrivés au point où l'utilisation de l'attente async est désormais une chose quotidienne. ALORS S'IL VOUS PLAÎT

Je vais refactoriser mon code car ce n'est pas respecté, mais du code moche en sortira car ce serait un hack.

Il me semble qu'autoriser la prise en charge asynchrone des méthodes de cycle de vie encouragera par défaut les mauvaises pratiques UX. Comment? Les fonctions asynchrones sont utilisées pour les demandes qui ne peuvent pas être exécutées immédiatement (par exemple, les demandes de longue durée ou de réseau). Forcer Vue à retarder la création ou le montage ou l'une des autres méthodes de cycle de vie à attendre votre demande de réseau ou un processus asynchrone de longue durée aura un impact notable sur l'utilisateur. Imaginez un utilisateur venant sur votre site et devant attendre 4 secondes avec un écran vide pendant que le composant attend que la connexion cellulaire irrégulière de l'utilisateur termine votre demande de réseau. Et non seulement cela a un impact négatif sur l'utilisateur, mais vous sacrifiez également votre contrôle sur la situation - en tant que développeur, vous ne pouvez rien faire pour que les utilisateurs perçoivent le temps de chargement plus rapidement ou pour afficher des indicateurs de progression déterminés ou indéterminés. Donc, en intégrant cette capacité dans Vue, vous ne faites pas du Web un meilleur endroit ; vous activez les mauvaises pratiques.

Bien mieux pour planifier et concevoir le cas asynchrone dès le départ : lancez votre processus asynchrone dans created ou mounted ou n'importe où, puis faites en sorte que votre composant ait une structure squelette ou au pire un spinner pendant que vous attendez que votre API renvoie les autorisations de l'utilisateur. Bien meilleure UX et vous ne sacrifiez aucun contrôle. Et Vue n'a pas besoin d'ajouter de code pour gérer les fonctions de cycle de vie asynchrones, en gardant le paquet plus petit. Gagner gagner.

Un avis sur des milliers. Ce n'est pas parce que vous ne pouvez pas imaginer un scénario dans lequel le rendu des composants retardé peut cohabiter avec une expérience utilisateur positive qu'il n'existe pas.

Si un framework combat le développeur, le développeur trouvera un autre framework.

@robob4him

Un avis sur des milliers. Ce n'est pas parce que vous ne pouvez pas imaginer un scénario dans lequel le rendu des composants retardé peut cohabiter avec une expérience utilisateur positive qu'il n'existe pas.

Si un framework combat le développeur, le développeur trouvera un autre framework

Vous avez tout à fait raison, mais rien de ce que vous avez partagé ici n'est un argument valable pour résoudre le problème d'une manière ou d'une autre. Vous avez introduit une tactique effrayante pour contraindre la communauté à développer une fonctionnalité. Pas une suite constructive de la conversation.

@wparad , c'est absolument une tactique de peur, mais cela ne forcera personne à faire quoi que ce soit. Présenter des arguments selon lesquels une fonctionnalité prend en charge les mauvaises habitudes ou les anti-modèles ou dégradera la communauté plus large des développeurs est tout autant une tactique effrayante.

La moitié des fonctionnalités de littéralement n'importe quel framework/langage sont dangereuses pour un développeur ; les méthodes publiques peuvent être étendues, Vue encourage l'accès à l'élément ($el), etc. Les frameworks fournissent ces choses car en fin de compte, le développeur doit faire le travail.

Cette demande de fonctionnalité date d'un an. Les gens doivent comprendre que la raison n'est pas en fait parce que cela entraînerait de mauvaises pratiques et qu'ils ne devraient pas non plus percevoir le rendu différé comme une mauvaise pratique.

J'ai besoin d'utiliser requirejs avec vue. Non pas que j'aime requirejs mais parce que je veux utiliser vue avec un LMS open source qui a tous ses modules en tant que modules AMD. Ce serait génial si je pouvais charger toutes les bibliothèques dont j'ai besoin dans le hook beforeCreate. L'alternative pour moi pour le moment est de les charger en dehors de vue, puis de les passer, ce qui est plus salissant.

Il me semble qu'autoriser la prise en charge asynchrone des méthodes de cycle de vie encouragera par défaut les mauvaises pratiques UX. Comment? Les fonctions asynchrones sont utilisées pour les demandes qui ne peuvent pas être exécutées immédiatement (par exemple, les demandes de longue durée ou de réseau). Forcer Vue à retarder la création ou le montage ou l'une des autres méthodes de cycle de vie à attendre votre demande de réseau ou un processus asynchrone de longue durée aura un impact notable sur l'utilisateur. Imaginez un utilisateur venant sur votre site et devant attendre 4 secondes avec un écran vide pendant que le composant attend que la connexion cellulaire irrégulière de l'utilisateur termine votre demande de réseau. Et non seulement cela a un impact négatif sur l'utilisateur, mais vous sacrifiez également votre contrôle sur la situation - en tant que développeur, vous ne pouvez rien faire pour que les utilisateurs perçoivent le temps de chargement plus rapidement ou pour afficher des indicateurs de progression déterminés ou indéterminés. Donc, en intégrant cette capacité dans Vue, vous ne faites pas du Web un meilleur endroit ; vous activez les mauvaises pratiques.

Bien mieux pour planifier et concevoir le cas asynchrone dès le départ : lancez votre processus asynchrone dans created ou mounted ou n'importe où, puis faites en sorte que votre composant ait une structure squelette ou au pire un spinner pendant que vous attendez que votre API renvoie les autorisations de l'utilisateur. Bien meilleure UX et vous ne sacrifiez aucun contrôle. Et Vue n'a pas besoin d'ajouter de code pour gérer les fonctions de cycle de vie asynchrones, en gardant le paquet plus petit. Gagner gagner.

Ce que vous dites, c'est comme si l'ajout de fonctionnalités v-if/v-clock/v-show encouragerait les mauvaises pratiques, nous ferions donc mieux de protéger le framework en supprimant ces fonctionnalités. Ensuite, utilisez une approche alambiquée pour faire de même afin que Vue soit plus petit pour ne pas mettre ces 3 directives. Les premiers développeurs ne sont pas stupides. La 2ème épreuve du cadre limite à son tour sa facilité d'utilisation car vous limitez ce qui peut être fait sur la base d'apparents « imbéciles ». Pourquoi mettre un v-if pour l'ensemble de son site Web pour le bloquer via des opérations asynchrones en laissant tout l'écran vide ?

Je pense que vous ignorez le fait que la plupart des cas d'utilisation peuvent même ne pas être avec une page blanche. Ils sont appelés composants pour une raison. Les cas où je souhaite personnellement l'utiliser sont ceux où quelque chose est déjà à l'écran en train de faire autre chose. C'est peut-être un composant bloqué par un v-if par exemple et déclenché lorsque quelque chose change. Cependant, lors du processus de rendu pour la première fois, async functions etc. doivent être respectés au démarrage du composant. J'ai parcouru toute la documentation de Vue à la recherche de cela et je l'ai finalement fait avec une solution de contournement très pas si jolie comme les exemples hackish ci-dessus.

Ce qui m'inquiète, c'est que quelqu'un/même plus tard me maintienne le code. C'est comme l'enfer de rappel Promise vs Async ... Attendez.

En fait, je le vois améliorer la flexibilité et la contrôlabilité du cadre d'une manière facile à suivre. Jetez un œil aux hacks ci-dessus pour voir ce que je veux dire. Les développeurs font tout cela juste pour combler le vide d'une simple déclaration async mounted () { await... } par exemple. Si vous ne voulez pas utiliser ces fonctionnalités, vous ne définissez simplement pas les fonctions comme async ou même n'utilisez pas du tout await .

En fait, quelqu'un qui utilisera réellement un crochet de cycle async mounted vie

@emahuni , je ne pense pas qu'aucun ne soit en désaccord avec les attentes que vous partagez, mais je pense qu'il y a une nuance, qui est laissée de côté. Supposons qu'un async mounted ou async created retarde le rendu du composant. Que fait le parent dans ce cas ? Est-ce que :

  • bloquer aussi
  • supposons que le composant est destiné à être supprimé du DOM v-if jusqu'à ce qu'il ait terminé le chargement
  • supposer que le composant est censé être caché jusqu'à la fin du chargement
  • Afficher un élément temporaire à sa place ?

Bien que je convienne que les attentes autour d'un composant chargé dynamiquement sont cohérentes, je ne pense pas que le comportement du parent le serait. Dans ces cas, l'OMI exposerait la mise en œuvre de l'enfant au parent et obligerait le parent à déterminer quoi faire avec ce composant dynamique. Au lieu de cela, la façon dont les données sont chargées et l'état du composant enfant doivent dépendre de l'enfant. S'il se charge de manière asynchrone, il a besoin d'un moyen d'expliquer à Vue ce qui doit être rendu à sa place (ou non rendu). La meilleure façon de gérer cela est de savoir comment le cadre fonctionne déjà, plutôt que d'introduire une nouvelle complexité.

De plus, je ne suis pas totalement votre argument cependant:

Ce que vous dites, c'est comme si l'ajout de fonctionnalités v-if/v-clock/v-show encouragerait les mauvaises pratiques, nous ferions donc mieux de protéger le framework en supprimant ces fonctionnalités.

Bien que dans ce cas, on peut voir clairement que l' introduction de composants async attendant le montage ou créé causera de mauvaises pratiques, il est clair que cela ne. Deuxièmement, cela soulève la question, même si celles-ci causent de mauvaises pratiques, nous devrions opter pour les corriger , plutôt que de les utiliser comme justification pour créer de nouvelles mauvaises pratiques. Si vous connaissez des mauvaises pratiques créées par v-if , etc... Je vous invite à partager explicitement (dans un autre numéro bien sûr) quel est le problème avec celles-ci, plutôt que d'essayer de l'utiliser comme justification pour une autre discussion.

@emahuni , je ne pense pas qu'aucun ne soit en désaccord avec les attentes que vous partagez, mais je pense qu'il y a une nuance, qui est laissée de côté. Supposons qu'un async mounted ou async created retarde le rendu du composant. Que fait le parent dans ce cas ? Est-ce que :

  • bloquer aussi

Le parent peut aller de l'avant et rendre sans même attendre l'enfant, pourquoi le ferait-il ? Il peut continuer et exécuter updated une fois que l'enfant a rendu.

  • supposons que le composant est destiné à être supprimé du DOM v-if jusqu'à ce qu'il ait terminé le chargement

Je ne suis pas sûr d'avoir compris cela... mais la réponse est non, nous ne supposons pas qu'il sera supprimé, il suffit de faire quelque chose pendant le montage qui doit bloquer le montage pendant ce temps. Il y a beaucoup de cas d'utilisation lus ci-dessus pour cela.

  • supposer que le composant est censé être caché jusqu'à la fin du chargement

Cela dépend du développeur, pourquoi il asynchrone le crochet monté ou tout autre crochet d'ailleurs.

  • Afficher un élément temporaire à sa place ?

Ce n'est peut-être pas du tout le cas. Encore une fois, cela dépend du développeur de ce qu'il a l'intention de réaliser. Le fait est que rien ne doit être en chemise droite. Lorsque, par exemple, v-if été conçu, ce n'est pas parce qu'ils ont conçu pourquoi quelqu'un voudrait bloquer le rendu d'un composant à chaque fois, et ce qu'il a placé à la place et l'a infaillible. Il y a beaucoup de choses qui peuvent mal tourner avec v-if par la conception du développeur. Vous ne devriez pas vous soucier de ce qui sera à l'écran pendant ce temps, ce n'est pas au framework de s'en soucier.

Bien que je convienne que les attentes autour d'un composant chargé dynamiquement sont cohérentes, je ne pense pas que le comportement du parent le serait. Dans ces cas, l'OMI exposerait la mise en œuvre de l'enfant au parent et obligerait le parent à déterminer quoi faire avec ce composant dynamique. Au lieu de cela, la façon dont les données sont chargées et l'état du composant enfant doivent dépendre de l'enfant. S'il se charge de manière asynchrone, il a besoin d'un moyen d'expliquer à Vue ce qui doit être rendu à sa place (ou non rendu). La meilleure façon de gérer cela est de savoir comment le cadre fonctionne déjà, plutôt que d'introduire une nouvelle complexité.

FYI: Vous acceptez que cela doit être mis en œuvre, cependant, cela introduira ces complexités dont vous pleurez et il pense que cela peut être fait plus tard lorsque des changements de rupture sont introduits plutôt que dans Vue 3. Le fait est qu'il pense que c'est nécessaire .

De plus, je ne suis pas totalement votre argument cependant:

Ce que vous dites, c'est comme si l'ajout de fonctionnalités v-if/v-clock/v-show encouragerait les mauvaises pratiques, nous ferions donc mieux de protéger le framework en supprimant ces fonctionnalités.

Bien que dans ce cas, on peut voir clairement que l' introduction de composants async attendant le montage ou créé causera de mauvaises pratiques, il est clair que cela ne. Deuxièmement, cela soulève la question, même si celles-ci causent de mauvaises pratiques, nous devrions opter pour les corriger , plutôt que de les utiliser comme justification pour créer de nouvelles mauvaises pratiques. Si vous connaissez des mauvaises pratiques créées par v-if , etc... Je vous invite à partager explicitement (dans un autre numéro bien sûr) quel est le problème avec celles-ci, plutôt que d'essayer de l'utiliser comme justification pour une autre discussion.

J'ai simplement indiqué ces directives comme un exemple de fonctionnalités qui peuvent être utilisées de manière incorrecte pour bloquer le rendu d'un composant similaire à ce que vous disiez à propos de async... . Rien de mal avec eux. Devrions-nous donc supprimer ces directives simplement parce que quelqu'un peut utiliser des "mauvaises pratiques" pour créer des composants qui affichent des pages blanches pendant une minute ? En fait, vous ne voyez personne faire cela parce que cela n'arrive pas, à moins que vous n'essayiez de donner un exemple d'extrême méchanceté comme dans affreux.

Écoutez, le fait est que si vous ne voyez pas encore de cas d'utilisation, ne dites pas que d'autres personnes l'utiliseront mal et que cela ne devrait donc pas être fait. Les mauvaises pratiques sont une question d'ignorance et une personne aussi ignorante peut ne jamais utiliser complètement ces fonctionnalités.

Quelqu'un a demandé ceci https://github.com/vuejs/vue/issues/7209#issuecomment -424349370 là-haut et personne ne lui a répondu d'après ce que j'ai vu. Cela montre de loin que Vue est à la traîne sur cette partie. Cette partie de l'architecture a été conçue lorsque l'async n'était pas encore une chose. Donc, le mettre à jour pour répondre aux exigences modernes des architectures modernes est certainement une bonne idée. Sinon, le reste est bidon et des solutions de contournement qui nécessitent des moyens spécifiques de le faire plutôt que de faire ce qui se passe dans l'industrie.

Cependant, je ne suis pas encore sûr, mais en regardant la nouvelle API fonctionnelle d'un coup d'œil, il semble qu'il soit réellement possible de le faire. Puisqu'il est fonctionnel, cela signifie que l'on peut faire certaines choses qu'ils ne pourraient pas faire objectivement, comme les hooks de cycle de vie asynchrones.

Écoutez, le fait est que si vous ne voyez pas encore de cas d'utilisation, ne dites pas que d'autres personnes l'utiliseront mal et que cela ne devrait donc pas être fait. Les mauvaises pratiques sont une question d'ignorance et une personne aussi ignorante peut ne jamais utiliser complètement ces fonctionnalités.

Jamais fait ce point, je fais le point que je veux effectuer des actions asynchrones par défaut sans jamais bloquer le rendu du composant. Il n'est pas intuitif que l'exécution d'actions async dans un bloc mounted ou created entraîne un retard du rendu du composant. En supposant que ce soit le cas, je verrais plutôt la complexité résulter de la façon dont un consommateur, qui souhaite la fonctionnalité actuelle, procéderait. L'argument jusqu'à présent n'est pas que ce qui est demandé ne peut pas être fait, c'est que ce qui est demandé devrait être la valeur par défaut. Vous pouvez déjà bloquer le rendu de votre composant en basculant le modèle affiché en fonction d'un v-if="loaded" .

Pour le moment, le rendu sans bloquer le code ressemble à ceci :
Pour l'instant ce code est :

<template>
  <div>  
    <spinner v-if="!loaded">
    <div v-else>
      ....
    </div>
</template>

<script>
  async created() { 
    await this.loadData();
    this.loaded = true;
  }
</script>

Et le rendu avec blocage ressemble exactement à la même chose, puisque vous ne pouvez pas réellement bloquer. En supposant que le async created() bloque réellement le composant. Ensuite, nous avons maintenant une séparation de code. Pour afficher le spinner, on écrit :

<template>
  <div>  
    <spinner v-if="!loaded">
    <div v-else>
      ....
    </div>
</template>

<script>
  created() { 
    this.loadData().then(() => this.loaded = true);
  }
</script>

et ignorez simplement le rendu du composant sur l'écran que nous écrivons

<template>
  <div>  
    <div>
      ....
    </div>
</template>

<script>
  async created() { 
    await this.loadData();
  }
</script>

En quoi l'ajout de cette simplification pour bloquer le rendu justifie-t-il de ne pas compliquer le blocage ? Je ne le vois tout simplement pas.

Gestion des dépendances pour les composants

@ yyx990803 Veuillez jeter un œil à ceci, ce n'est pas parfait, mais néanmoins un scénario de cas d'utilisation complexe.

Ok, voici un cas d'utilisation qui aurait pu être traité avec élégance si les hooks de cycle de vie étaient asynchrone... attendez :
_Je souhaitais en fait le faire dans une application et j'ai obtenu un code moche pour y parvenir. Ceci est un exemple très artificiel de coz_

J'ai besoin que le composant A attende que les crochets mounted du composant B && C se déclenchent avant de se monter. Ainsi, le mounted composant A doit attendre son crochet created , ce qui a déclenché le montage du composant B && C qui attendaient le déclencheur _(qui peut en fait faire quelque chose avant d'attendre)_. Le truc, c'est que c'est plus facile de cette façon et beaucoup plus propre et intuitif car tout reste au même endroit pour le composant concerné.

A émet un événement de déclenchement et à l' écoute des réponses B et C _ (B et C attente pour le signal de A avant de continuer, puis émettre des événements une fois montés) _ avant de poursuivre, simple. Cela ressemble plus à un scénario de dépendance pour les composants sans aucune donnée étrangère jonchée ailleurs pour la gestion de l'état.

Composant d'hébergement principal , tous les composants sont chargés ensemble, mais attendez les bons en utilisant des événements et async... attendez. Peu importe ce que font ces enfants, ils s'ordonnent eux-mêmes.

<template>
  <div>
     ...
     <component-A/>
     <component-B/>
     <component-C/>
     ... other conent
  </div>
</template>
<script>
  import ComponentA...
  ...
  export default {
      components: { ComponentA... }
  }
</script>

le composant A contrôle le montage de B et C en tant que dépendances. Le déclenchement pourrait se faire ultérieurement dans d'autres hooks ou même via un événement UX des mêmes composants. Ce qui suit est juste pour montrer l'idée ici.

<template>
  ...
</template>
<script>
  export default {
      async created() {
          // ...do something here before mounting thrusters, even await it
         this.root.$emit('mount-thrusters');
         await Promise.all([
            this.wasMounted('thruster-1-mounted'), 
            this.wasMounted('thruster-2-mounted')
         ]); 
      },
      mounted() {
        // will only run after components B and C have mounted
        ...
      },

     methods: {
       wasMounted(compEvent) {
          return new Promise( (resolve)=>this.root.$once(compEvent, ()=>resolve()));
       }
    }
  }
</script>

composant B

<template>
  ...
</template>
<script>
  export default {
      async created() {
          // ...do something here, even await it, but happens at the same time as all components
         await new Promise( (resolve)=>this.root.$once('mount-thrusters', ()=>resolve()));
      },
     mounted() {
       this.root.$emit('thruster-1-mounted');
    }
  }
</script>

composant C

<template>
  ...
</template>
<script>
  export default {
      async created() {
          // ...do something here, even await it, but happens at the same time as all components
         await new Promise( (resolve)=>this.root.$once('mount-thrusters', ()=>resolve()));
      },
     mounted() {
       this.root.$emit('thruster-2-mounted');
    }
  }
</script>

Le code ci-dessus peut être encore simplifié par des mixins car il y a beaucoup d'extraits de code en double, je voulais juste que ce soit clair. La méthode wasMounted peut être mise en conserve dans un mixin et utilisée sur les 3 composants.

Ici, nous pouvons clairement voir ce que chaque composant attend sans aucun autre code hackish ou routeur jonché ailleurs pour contrôler la même chose. C'est très déroutant de le faire sans cette fonctionnalité, croyez-moi, je l'ai fait dans une application.

Cela élimine évidemment le code inutilement complexe et impossible à maintenir.

Imaginez maintenant cela dans une grande application, avec 32 composants de propulseur qui se comportent différemment. Vous n'aurez à l'esprit qu'environ 3 points, qui sont réductibles à 2 même si vous y jetez des mixins.

Faire en sorte que les choses restent fraîches

Bien sûr, cela n'est pas seulement limité au montage et à la création, mais devrait en fait fonctionner avec tous les autres crochets de cycle de vie. Imaginez si c'est dans un tout nouveau crochet beforeActivate / beforeUpdate . Nous pourrions faire en sorte que le composant attende _activation/update_ et uniquement _activate/update_ lorsque le rafraîchissement est effectué à chaque fois que le composant est _activé/mis à jour_ ; s'assurer que les choses restent fraîches.

La liste est interminable une fois que cela est mis en œuvre.

Écoutez, le fait est que si vous ne voyez pas encore de cas d'utilisation, ne dites pas que d'autres personnes l'utiliseront mal et que cela ne devrait donc pas être fait. Les mauvaises pratiques sont une question d'ignorance et une personne aussi ignorante peut ne jamais utiliser complètement ces fonctionnalités.

Jamais fait ce point, je fais le point que je veux effectuer des actions asynchrones par défaut sans jamais bloquer le rendu du composant. Il n'est pas intuitif que l'exécution d'actions async dans un bloc mounted ou created entraîne un retard du rendu du composant. En supposant que ce soit le cas, je verrais plutôt la complexité résulter de la façon dont un consommateur, qui souhaite la fonctionnalité actuelle, procéderait. L'argument jusqu'à présent n'est pas que ce qui est demandé ne peut pas être fait, c'est que ce qui est demandé devrait être la valeur par défaut. Vous pouvez déjà bloquer le rendu de votre composant en basculant le modèle affiché en fonction d'un v-if="loaded" .

Pour le moment, le rendu sans bloquer le code ressemble à ceci :
Pour l'instant ce code est :

<template>
  <div>  
    <spinner v-if="!loaded">
    <div v-else>
      ....
    </div>
</template>

<script>
  async created() { 
    await this.loadData();
    this.loaded = true;
  }
</script>

Et le rendu avec blocage ressemble exactement à la même chose, puisque vous ne pouvez pas réellement bloquer. En supposant que le async created() bloque réellement le composant. Ensuite, nous avons maintenant une séparation de code. Pour afficher le spinner, on écrit :

<template>
  <div>  
    <spinner v-if="!loaded">
    <div v-else>
      ....
    </div>
</template>

<script>
  created() { 
    this.loadData().then(() => this.loaded = true);
  }
</script>

et ignorez simplement le rendu du composant sur l'écran que nous écrivons

<template>
  <div>  
    <div>
      ....
    </div>
</template>

<script>
  async created() { 
    await this.loadData();
  }
</script>

En quoi l'ajout de cette simplification pour bloquer le rendu justifie-t-il de ne pas compliquer le blocage ? Je ne le vois tout simplement pas.

C'est le bootcamp de Vue 101 et il n'y a rien de nouveau là-dedans... ce n'est pas suffisant pour couvrir les cas ci-dessus par exemple. L'idée ici est de réduire la complexité dans l'espace utilisateur où Vue est réellement utilisé et de disposer d'un code plus facile à raisonner.

Le rendu ici n'est pas le problème, c'est ce qui se passe avant le rendu qui a en fait une conséquence. Nous voulons la liberté de faire les choses avant de procéder ou de rendre un composant. Quoi qu'il en soit, cela n'a rien à voir avec le blocage du rendu. Il existe de nombreux hooks de cycle de vie pris en charge par Vue et ceux-ci peuvent être utiles s'il existait un moyen de gérer le code asynchrone par rapport à d'autres hooks. L'idée est que Vue respecte l'async en interne avant de passer à la prochaine fonction de crochet.

Je suis vraiment confus par cela, au lieu de coupler B & C à A, je déplacerais le code pour "charger A" dans A.js, puis A.js mettrait à jour les "B.data" et "C.data" . De cette façon, si A.data change pour une raison quelconque, les autres composants sont automatiquement restitués plutôt que d'essayer de déléguer
contrôle d'un composant à un autre. Même dans le cas partagé, je découplerais toujours les données pour rendre A du composant A . Nous avons utilisé une seule classe qui contient des méthodes comme fetchData et hasInitialized pour lesquelles la dernière est une promesse et la première résout la seconde.
Le couplage direct des composants crée des arborescences de dépendances inattendues qui empêchent les composants d'être réutilisables et permettent à vue de les restituer correctement.

Alternativement, je voudrais même emit l'événement directement au parent de A, B et C, et non sur la portée globale, et laisser le parent décider s'il doit rendre B et C, c'est-à-dire

  <template>
    <a-component @rendered="showBandC = true" />
    <b-component v-if="showBandC" />
    <c-component v-if="showBandC" />
  </template>

Qu'est-ce qu'il y a à propos de A que dans ce cas nous aurions réellement besoin de rendre avant que B et C ne soient rendus. S'il y a des trucs dans la méthode created() , rien n'empêche que ça remplisse le magasin via une classe javascript ou en utilisant un module store . Mais le cas d'utilisation explicite serait plus utile, c'est-à-dire quel est l'UX de la user story qui ne peut pas être capturé ?

L'idée est que Vue respecte l'async en interne avant de passer à la prochaine fonction de crochet.

Bien sûr, cette partie a du sens, mais je ne sais pas pourquoi l'exemple doit être alambiqué, pourquoi ne pas simplement dire quelque chose comme cette histoire d'utilisateur :

Mon composant a à la fois async beforeMount et async mounted , mais le code dans mounted se déclenche avant que le code dans beforeMount soit terminé. Comment pouvons-nous empêcher mounted de tirer avant que beforeMount soit terminé ?

C'est en quelque sorte ce qu'était la demande initiale, donc la question qui a été posée dans la deuxième réponse, je pense, est toujours pertinente : https://github.com/vuejs/vue/issues/7209#issuecomment -350284784

Clôture pour le moment, mais n'hésitez pas à poursuivre avec des raisonnements / cas d'utilisation / implications plus concrets.

Existe-t-il réellement un cas d'utilisation valable pour avoir besoin de bloquer les hooks de cycle de vie précédemment exécutés, ou est-il correct que les hooks de cycle de vie soient synchrones. Jusqu'à présent, la discussion a été de nature philosophique (comme les bonnes discussions sur l'architecture ont tendance à le faire), mais en réalité, la question est de savoir s'il y avait une bonne raison de le faire. Je ne doute pas une seconde qu'il soit raisonnable que le framework attende du code asynchrone. J'ai eu le problème exact dans N autres bibliothèques qui ne l'ont pas fait ou n'ont pas passé de rappel (ou ont passé un rappel mais n'ont pas passé de rappel au rappel). Cependant, il est en fait raisonnable d'avoir un hook de cycle de vie asynchrone ou les raisons sont-elles le résultat d'essayer de faire quelque chose qui "ne devrait pas être fait" ?

C'est-à-dire que se passe-t-il lorsque vous essayez de unmount un composant qui n'a pas fini d'être created , wow, ce serait mal d'attendre ça encore. Ou destroying un qui n'a pas fini d'être mounted , je n'envie pas l'implémenteur de cette fonctionnalité.

Je suis vraiment confus par cela, au lieu de coupler B & C à A, je déplacerais le code pour "charger A" dans A.js, puis A.js mettrait à jour les "B.data" et "C.data" . De cette façon, si A.data change pour une raison quelconque, les autres composants sont automatiquement restitués plutôt que d'essayer de déléguer

C'est une complexité accrue, une mauvaise pratique. Essayez de l'écrire ici en entier, voyons ce que vous voulez dire, mais pour moi, vous venez d'augmenter considérablement la complexité.

contrôle d'un composant à un autre. Même dans le cas partagé, je découplerais toujours les données pour rendre A du composant A . Nous avons utilisé une seule classe qui contient des méthodes comme fetchData et hasInitialized pour lesquelles la dernière est une promesse et la première résout la seconde.

C'est maintenant trop coupler les composants. Nous voulons que ceux-ci fonctionnent sans l'autre, sauf pour A.

Le couplage direct des composants crée des arborescences de dépendances inattendues qui empêchent les composants d'être réutilisables et permettent à vue de les restituer correctement.

En fait, vous manquez le point, ceux-ci sont faiblement couplés dans la mesure où chacun peut être utilisé et entretenu sans affecter l'autre. En fait, vous pouvez les déposer plusieurs fois n'importe où sans écrire plus de code dans les parents en dehors de <component-x /> , pas de v-if , vuex pour gérer son état ni rien d'autre.

Je les ai couplés A à B et C juste pour démontrer, j'aurais pu diviser cela gentiment ou simplement compter le nombre de composants qui ont répondu, puis continuer lorsqu'un certain nombre attendu est atteint (2 dans ce cas), par exemple :

 this.$root.$emit('thruster-mounted') // in B and C
// instead of 
 this.$root.$emit('thruster-1-mounted') // for B and 2 for C

// then count the responses and resolve once they are >= 2 in component A

Quoi qu'il en soit, c'est pourquoi j'ai dit la gestion des dépendances des composants, cela est souhaité et attendu, mais doit être fait avec le moins de complexité possible car plus cela devient complexe, plus cela devient déroutant.

Alternativement, je voudrais même emit l'événement directement au parent de A, B et C, et non sur la portée globale, et laisser le parent décider s'il doit rendre B et C, c'est-à-dire

Je savais que tu allais dire ça, mais ce n'est pas souhaité. Ces composants ne doivent être contrôlés par rien d'autre, j'ai donc souligné que le composant principal se soucie moins de ce que font ses enfants, ils doivent rester indépendants. Je veux pouvoir les placer n'importe où dans l'application et faire fonctionner la même chose . Au moment où vous faites ce que vous dites là, regardez comment tout se brise. Mais je peux facilement mettre des composants n'importe où dans l'arborescence DOM sans même broncher, et tout fonctionnera. Je peux même en avoir plusieurs copies et cela fonctionnera toujours. Tout cela grâce à async ... await sur les cycles de vie.

  <template>
    <a-component @rendered="showBandC = true" />
    <b-component v-if="showBandC" />
    <c-component v-if="showBandC" />
  </template>

Qu'est-ce qu'il y a à propos de A que dans ce cas nous aurions réellement besoin de rendre avant que B et C ne soient rendus.

Nous voulons que chaque composant effectue un travail unique avant d'être monté. Mais tous les composants ne seront rendus que lorsque tous les autres composants auront fini de faire ce travail. C'est l'histoire ici.
Alors bonne chance avec la gestion de l'état et v-if juste pour contrôler cela sans parler des données réelles qu'il produira probablement dans le magasin vuex . Vous finirez par avoir beaucoup de code en double écrit partout où le composant est utilisé. Disons que vous avez un autre composant qui héberge A et C uniquement et dans une configuration différente ? Vous devez écrire ces v-if , la gestion de l'état vuex pour que cela fonctionne. Voyez-vous le problème? permettez-moi d'illustrer :

  // component Z totally different from foo, so we can't make this a component for reuse
  <template>
    <a-component @rendered="showBandC = true" />
    <c-component v-if="showBandC" />
  </template>
 ...
computed: {
showBandB() { ...vuex ...,
showBandC() { ...vuex ...,
}
  // component Foo totally unique, probably has other things it does
  <template>
    <b-component v-if="showBandC" />
    <c-component v-if="showBandC" />
  </template>
 ...
computed: {
// ok you could mixin this, fair, but complex nevertheless
showBandB() { ...vuex ...,
showBandC() { ...vuex ...,
}

..... et ainsi de suite

au lieu d'utiliser simplement les composants sans jamais rien faire dans les parents comme ceci :

  // component Z
  <template>
    <a-component />
    <b-component />
  </template>
  // component Foo
  <template>
    <b-component  />
    <c-component />
  </template>

... et ainsi de suite

S'il y a des trucs dans la méthode created() , rien n'empêche que ça remplisse le store via une classe javascript ou en utilisant un module store . Mais le cas d'utilisation explicite serait plus utile, c'est-à-dire quel est l'UX de la user story qui ne peut pas être capturé ?

Rappelez-vous ce que j'ai dit à propos de la gestion de l'État jonchée de partout? Nous ne voulons pas que la gestion de ces composants signifie que nous allons gérer beaucoup de choses ailleurs, ce qui est très complexe au lieu de ce que je viens de faire. En plus, ça ne fera pas ce que je veux que ça fasse ; ne montez que lorsque chaque composant a terminé ce qu'il est censé faire avec très peu de complexité.

L'idée est que Vue respecte l'async en interne avant de passer à la prochaine fonction de crochet.

Bien sûr, cette partie a du sens, mais je ne sais pas pourquoi l'exemple doit être alambiqué, pourquoi ne pas simplement dire quelque chose comme cette histoire d'utilisateur :

Mon composant a à la fois async beforeMount et async mounted , mais le code dans mounted se déclenche avant que le code dans beforeMount soit terminé. Comment pouvons-nous empêcher mounted de tirer avant que beforeMount soit terminé ?

Le fait est que nous voulons que les choses se passent avant que l'un de ces composants ne soit rendu et qu'il soit utilisable sans trop d'entrée en dehors des composants eux-mêmes. C'est une chose réelle que j'ai remarquée, j'aurais pu faire mieux s'il y avait eu une application que nous créons. J'ai fini par écrire du code qui contient beaucoup de mixins, de vuesx et de parents fortement couplés partout (en utilisant des mixins), les composants ont été utilisés parce que cela manque. Ce n'est pas une histoire alambiquée, je vous raconte en fait ce qui s'est passé d'une manière simple. Nous avons dû repenser la façon dont l'interface utilisateur était censée être conçue et maintenue. C'est devenu un peu moins intéressant visuellement et beaucoup plus complexe au niveau du code.

Voyez-vous combien de choses complexes vous avez introduites dans cette configuration simple qui vient d'être résolue par cette petite fonctionnalité d'attente asynchrone dans les cycles de vie ?

@wparad

C'est-à-dire que se passe-t-il lorsque vous essayez de démonter un composant qui n'a pas fini d'être créé, wow, ce serait mal d'attendre ça encore. Ou en détruisant un qui n'a pas fini d'être monté, je n'envie pas l'implémenteur de cette fonctionnalité.

C'est ici que:

... nécessite une refonte/réécriture fondamentale de l'architecture à réaliser, et peut potentiellement casser beaucoup de logique qui repose sur la nature synchrone des crochets de cycle de vie...

... partie que je crois a été mentionnée par vous. Nous avons besoin d'un moyen de gérer ces cas extrêmes et probablement plus que cela introduit. En d'autres termes, nos intentions doivent fonctionner sans faire planter l'application.

Dans tous ces cas, j'utiliserais des délais d'attente pour vérifier ou annuler l'attente, un peu comme la plupart des opérations asynchrones qui peuvent échouer.

Mais si vous regardez attentivement l'exemple que j'ai présenté, vous verrez ce que je veux dire. Cela aurait pu résoudre beaucoup de choses, sans beaucoup de code écrit nulle part. En fait, notre application aurait pu être bien meilleure avec une base de code beaucoup plus petite, défragmentée et facile à saisir si cela avait été possible.
Le problème avec ce genre de fonctionnalités est que vous ne voyez leur importance qu'une fois que vous rencontrez un barrage routier. Je suis arrivé sur cette page pour la première fois lorsque nous avions pensé à de nombreuses solutions de contournement pour cet exemple que je viens de donner. Je n'étais pas venu chercher de l'aide, c'était pour déposer une demande de fonctionnalité puisque nous avons remarqué qu'elle n'était pas disponible dans Vue.

Cette page vous a été utile?
0 / 5 - 0 notes