Aperçu
Dans Vue.js 2.x, model.sync
sera obsolète .
Alors, quelle est la bonne façon de communiquer entre les composants frères dans Vue.js 2.x ?
Contexte
Si je comprends bien Vue 2.x, la méthode préférée pour la communication entre frères et sœurs est d'utiliser un magasin ou un bus d'événements .
Selon Evan (créateur de Vue):
Il convient également de mentionner que «passer des données entre les composants» est généralement une mauvaise idée, car à la fin le flux de données devient impossible à suivre et très difficile à déboguer.
Si une donnée doit être partagée par plusieurs composants, préférez les magasins globaux ou Vuex .
Et:
.once
et.sync
sont obsolètes. Les accessoires sont désormais toujours à sens unique. Pour produire des effets secondaires dans la portée parent, un composant doit explicitementemit
un événement au lieu de s'appuyer sur une liaison implicite.
Ainsi, Evan suggère d' utiliser $emit()
et $on()
.
Préoccupations
Ce qui m'inquiète, c'est:
- Chaque
store
etevent
a une visibilité globale (corrigez - moi si je me trompe); - Il est trop coûteux de créer un nouveau magasin pour chaque communication mineure;
Ce que je veux, c'est une certaine portée events
ou stores
visibilité pour les composants frères. (Ou peut-être que je n'ai pas compris l'idée ci-dessus.)
Question
Alors, quelle est la bonne façon de communiquer entre les composants frères?
la source
$emit
combiné avecv-model
pour émuler.sync
. Je pense que vous devriez suivre la voieRéponses:
Avec Vue 2.0, j'utilise le mécanisme eventHub comme démontré dans la documentation .
Définissez un hub d'événements centralisé.
Maintenant, dans votre composant, vous pouvez émettre des événements avec
Et pour vous écouter
Mise à jour Veuillez consulter la réponse de @alex , qui décrit une solution plus simple.
la source
this.$root.$emit()
etthis.$root.$on()
Vous pouvez même le raccourcir et utiliser l'
Vue
instance racine comme hub d'événements global:Composant 1:
Composant 2:
la source
Types de communication
Lors de la conception d'une application Vue (ou en fait de toute application basée sur des composants), il existe différents types de communication qui dépendent des préoccupations auxquelles nous sommes confrontés et ils ont leurs propres canaux de communication.
Logique métier: fait référence à tout ce qui est spécifique à votre application et à son objectif.
Logique de présentation: tout ce avec quoi l'utilisateur interagit ou qui résulte de l'interaction de l'utilisateur.
Ces deux préoccupations sont liées à ces types de communication:
Chaque type doit utiliser le bon canal de communication.
Canaux de communication
Un canal est un terme vague que j'utiliserai pour faire référence à des implémentations concrètes pour échanger des données autour d'une application Vue.
Accessoires: logique de présentation parent-enfant
Le canal de communication le plus simple de Vue pour une communication directe parent-enfant . Il doit principalement être utilisé pour transmettre des données relatives à la logique de présentation ou à un ensemble restreint de données dans la hiérarchie.
Réfs et méthodes: Présentation anti-motif
Quand cela n'a pas de sens d'utiliser un accessoire pour laisser un enfant gérer un événement d'un parent, configurer unref
sur le composant enfant et appeler ses méthodes est très bien.Ne faites pas ça, c'est un anti-modèle. Repensez l'architecture de vos composants et le flux de données. Si vous souhaitez appeler une méthode sur un composant enfant d'un parent, il est probablement temps de lever l'état ou d'envisager les autres méthodes décrites ici ou dans les autres réponses.
Evénements: logique de présentation enfant-parent
$emit
et$on
. Le canal de communication le plus simple pour une communication directe enfant-parent. Encore une fois, devrait être utilisé pour la logique de présentation.Bus événementiel
La plupart des réponses offrent de bonnes alternatives pour le bus d'événements, qui est l'un des canaux de communication disponibles pour les composants distants, ou quoi que ce soit en fait.
Cela peut devenir utile lorsque vous passez des accessoires partout, de loin vers le bas, à des composants enfants profondément imbriqués, presque aucun autre composant n'en ayant besoin entre les deux. Utilisez avec parcimonie pour des données soigneusement sélectionnées.
Attention: la création ultérieure de composants qui se lient au bus d'événements sera liée plus d'une fois, ce qui entraînera le déclenchement de plusieurs gestionnaires et des fuites. Personnellement, je n'ai jamais ressenti le besoin d'un bus événementiel dans toutes les applications à page unique que j'ai conçues dans le passé.
Ce qui suit montre comment une simple erreur conduit à une fuite où le
Item
composant se déclenche toujours même s'il est supprimé du DOM.Afficher l'extrait de code
N'oubliez pas de supprimer les auditeurs dans le
destroyed
hook de cycle de vie.Magasin centralisé (logique métier)
Vuex est la voie à suivre avec Vue pour la gestion de l'état . Il offre bien plus que de simples événements et il est prêt pour une application à grande échelle.
Et maintenant vous demandez :
Ça brille vraiment quand:
Ainsi, vos composants peuvent vraiment se concentrer sur ce qu'ils sont censés être, en gérant les interfaces utilisateur.
Cela ne signifie pas que vous ne pouvez pas l'utiliser pour la logique des composants, mais j'appliquerais cette logique à un module Vuex à espace de noms avec uniquement l'état d'interface utilisateur global nécessaire.
Pour éviter de gérer un gros désordre de tout dans un état global, le magasin doit être séparé en plusieurs modules d'espacement de noms.
Types de composants
Pour orchestrer toutes ces communications et pour faciliter la réutilisation, nous devons considérer les composants comme deux types différents.
Encore une fois, cela ne signifie pas qu'un composant générique doit être réutilisé ou qu'un conteneur spécifique à une application ne peut pas être réutilisé, mais ils ont des responsabilités différentes.
Conteneurs spécifiques à l'application
Ce ne sont que de simples composants Vue qui enveloppent d'autres composants Vue (conteneurs génériques ou spécifiques à d'autres applications). C'est là que la communication du magasin Vuex doit avoir lieu et ce conteneur doit communiquer par d'autres moyens plus simples comme les accessoires et les écouteurs d'événements.
Ces conteneurs peuvent même ne contenir aucun élément DOM natif et laisser les composants génériques gérer la création de modèles et les interactions des utilisateurs.
C'est là que se déroule le cadrage. La plupart des composants ne connaissent pas le magasin et ce composant devrait (principalement) utiliser un module de magasin avec un espace de noms limité
getters
etactions
appliqué avec le assistants de liaison Vuex .Composants génériques
Ceux-ci devraient recevoir leurs données des accessoires, apporter des modifications à leurs propres données locales et émettre des événements simples. La plupart du temps, ils ne devraient pas savoir du tout qu'un magasin Vuex existe.
Ils pourraient également être appelés conteneurs, car leur seule responsabilité pourrait être de les envoyer à d'autres composants de l'interface utilisateur.
Communication fraternelle
Alors, après tout cela, comment devrions-nous communiquer entre deux composants frères?
C'est plus facile à comprendre avec un exemple: disons que nous avons une zone de saisie et que ses données doivent être partagées dans l'application (frères et sœurs à différents endroits de l'arborescence) et persistantes avec un backend.
En commençant par le pire des cas , notre composant mélangerait présentation et logique métier .
Pour séparer ces deux préoccupations, nous devons envelopper notre composant dans un conteneur spécifique à l'application et conserver la logique de présentation dans notre composant d'entrée générique.
Notre composant d'entrée est désormais réutilisable et ne connaît ni le backend ni les frères et sœurs.
Notre conteneur spécifique à l'application peut désormais servir de pont entre la logique métier et la communication de présentation.
Étant donné que les actions du magasin Vuex concernent la communication du backend, notre conteneur n'a pas besoin de connaître axios et le backend.
la source
D'accord, nous pouvons communiquer entre frères et sœurs via les parents en utilisant des
v-on
événements.Supposons que nous voulons mettre à jour le
Details
composant lorsque nous cliquons sur un élément dansList
.dans
Parent
:Modèle:
Ici:
v-on:select-item
c'est un événement, qui sera appelé enList
composant (voir ci-dessous);setSelectedItem
c'est uneParent
méthode de mise à jourselectedModel
;JS:
Dans
List
:Modèle:
JS:
Ici:
this.$emit('select-item', item)
enverra l'article viaselect-item
directement dans le parent. Et le parent l'enverra à laDetails
vuela source
Ce que je fais habituellement si je veux "pirater" les modèles normaux de communication dans Vue, spécialement maintenant qui
.sync
est obsolète, est de créer un simple EventEmitter qui gère la communication entre les composants. De l'un de mes derniers projets:Avec cet
Transmitter
objet, vous pouvez alors faire, dans n'importe quel composant:Et pour créer un composant "récepteur":
Encore une fois, c'est pour des utilisations vraiment spécifiques. Ne basez pas toute votre application sur ce modèle, utilisez
Vuex
plutôt quelque chose comme .la source
vuex
, mais encore une fois, dois-je créer le magasin de vuex pour chaque communication mineure?vuex
oui, allez-y. Utilise le.La manière de gérer la communication entre frères et sœurs dépend de la situation. Mais d'abord, je tiens à souligner que l'approche globale du bus d'événements disparaît dans Vue 3 . Voir cette RFC . C'est pourquoi j'ai décidé d'écrire une nouvelle réponse.
Motif ancestral commun le plus bas (ou «LCA»)
Pour les cas simples, je recommande fortement d'utiliser le modèle de l'ancêtre commun le plus bas (également connu sous le nom de «données vers le bas, événements vers le haut»). Ce modèle est facile à lire, implémenter, tester et déboguer.
En substance, cela signifie que si deux composants doivent communiquer, placez leur état partagé dans le composant le plus proche que les deux partagent en tant qu'ancêtre. Passez les données du composant parent au composant enfant via les accessoires et transmettez les informations de l'enfant au parent en émettant un événement (voir l'exemple au bas de cette réponse).
Pour un exemple artificiel, dans une application de messagerie, si le composant "À" devait interagir avec le composant "corps du message", l'état de cette interaction pourrait vivre dans leur parent (peut-être un composant appelé
email-form
). Vous pourriez avoir un accessoire dans leemail-form
appeléaddressee
afin que le corps du message puisse automatiquement ajouterDear {{addressee.name}}
à l'e-mail en fonction de l'adresse e-mail du destinataire.L'ACV devient onéreuse si la communication doit parcourir de longues distances avec de nombreux composants intermédiaires. Je renvoie souvent mes collègues à cet excellent article de blog . (Ignorez le fait que ses exemples utilisent Ember; ses idées sont applicables dans de nombreux frameworks d'interface utilisateur.)
Modèle de conteneur de données (par exemple, Vuex)
Pour les cas complexes ou les situations où la communication parent-enfant impliquerait trop d'intermédiaires, utilisez Vuex ou une technologie de conteneur de données équivalente. Le cas échéant, utilisez modules d'espacement de noms .
Par exemple, il peut être raisonnable de créer un espace de noms distinct pour une collection complexe de composants avec de nombreuses interconnexions, comme un composant de calendrier complet.
Modèle de publication / abonnement (bus d'événements)
Si le modèle de bus d'événements (ou «publier / souscrire») est plus approprié pour vos besoins, l'équipe de base de Vue recommande maintenant d'utiliser une bibliothèque tierce telle que mitt . (Voir la RFC référencée au paragraphe 1.)
Randonnées et code bonus
Voici un exemple de base de la solution de l'ancêtre commun le plus bas pour la communication entre frères et sœurs, illustrée via le jeu whack-a-mole .
Une approche naïve pourrait être de penser: «la taupe 1 devrait indiquer à la taupe 2 d'apparaître après avoir été frappée». Mais Vue déconseille ce type d'approche, car il veut que nous pensions en termes de structures arborescentes .
C'est probablement une très bonne chose. Une application non triviale où les nœuds communiquent directement entre eux à travers les arbres DOM serait très difficile à déboguer sans une sorte de système de comptabilité (comme le fournit Vuex). En plus de cela, les composants qui utilisent «les données vers le bas, les événements vers le haut» ont tendance à présenter un faible couplage et une réutilisation élevée, deux caractéristiques hautement souhaitables qui aident les grandes applications à évoluer.
Dans cet exemple, lorsqu'une taupe est frappée, elle émet un événement. Le composant du gestionnaire de jeu décide du nouvel état de l'application et, par conséquent, la taupe sœur sait quoi faire implicitement après le nouveau rendu de Vue. C'est un exemple assez trivial de «plus petit ancêtre commun».
la source