Vue: Autoriser les enfants à "hériter" des composants enregistrés pour les parents.

Créé le 10 sept. 2015  ·  36Commentaires  ·  Source: vuejs/vue

Je pense que cette fonctionnalité a été supprimée intentionnellement, mais dans certains cas, elle peut être très utile. Pourquoi ne pas remettre cette chose en la rendant probablement facultative ?

Commentaire le plus utile

Les importer explicitement est une répétition intéressante. Il vous permet de regarder n'importe quel composant de cette hiérarchie seul et de comprendre d'où viennent ses dépendances. Avec le repli implicite, vous ne vous souviendrez plus où vous avez importé ces composants dans la hiérarchie 3 mois plus tard.

Tous les 36 commentaires

Un cas d'utilisation dans le monde réel ?

Dans les petits cas d'utilisation, enregistrez simplement tout globalement ; dans les applications volumineuses, il est beaucoup plus facile à gérer pour chaque composant de dépendre explicitement de ce dont il a besoin. Cela peut être utile dans certaines situations, mais les avantages s'accompagnent d'un compromis global. L'idée derrière la version 1.0 est que "si quelque chose n'est que marginalement utile, ou a des implications négatives sur la maintenabilité, supprimons-le".

Ouais. Mon application a une fenêtre contextuelle ayant une structure complexe d'éléments d'éditeur personnalisés. Ces éléments peuvent être combinés dans une hiérarchie de 3 à 4 niveaux, ce qui rend inefficace leur déclaration explicite pour chaque élément parent (3 à 5 types parents * 10 éléments déclarés = 50 lignes de code répétitif). Et, il n'est pas non plus bon de les enregistrer globalement car ils n'apparaîtront jamais dans d'autres parties de l'application. J'aimerais donc qu'ils soient chargés "localement".

Les importer explicitement est une répétition intéressante. Il vous permet de regarder n'importe quel composant de cette hiérarchie seul et de comprendre d'où viennent ses dépendances. Avec le repli implicite, vous ne vous souviendrez plus où vous avez importé ces composants dans la hiérarchie 3 mois plus tard.

@yyx990803 J'ai une bonne mémoire, merci. Je retiendrai donc que mon application se compose de deux éléments très différents, chacun d'eux enregistrant un ensemble bien défini de composants qui lui sont propres. Je préférerais donc avoir le choix de l'endroit où charger mes actifs (et je soupçonne que la même chose s'est produite pour les directives et les filtres personnalisés).

Permettez-moi de partager mon impression. J'ai travaillé avec 0.12.x et ça s'est TRÈS bien passé (moins quelques petites choses de courbe d'apprentissage). API minimaliste et propre, syntaxe, code fiable. Maintenant, nous sommes à 1.0.0-beta et c'est devenu PIRE, pas mieux. Plus de répétition de code, les fonctionnalités que j'utilise supprimées me font réécrire le même code encore et encore. Je commence à penser que j'ai fait une erreur en choisissant Vue plutôt que React, car je ne suis pas sûr qu'il n'y aura plus de changements de rupture et de perte de temps à l'avenir.

@karevn

  1. Il serait beaucoup plus utile de lister les éléments spécifiques qui ont aggravé l'expérience de développement en plus de ce problème.
  2. 1.0.0-alpha sont des pré-versions, ce qui signifie qu'il n'y avait aucune garantie de stabilité de l'API en premier lieu. Si vous accordez de l'importance à la stabilité, vous devez vous en tenir à la version 0.12 et attendre la version 1.0 stable (qui aura également une version de migration finale). L'utilisation d'une version préliminaire signifie que vous avez accepté de faire face à des changements constants avec rupture.
  3. 1.0.0-beta n'est même pas sorti. Ce n'est probablement pas une bonne idée d'utiliser une branche non publiée, en cours de développement.
  4. Je conçois l'API en fonction de mon expérience et des commentaires de toute la communauté. Vous avez droit à tout ce que vous pensez et n'hésitez pas à passer à d'autres frameworks si les changements ne vont pas dans le sens que vous aimez. (En fait, dans React, vous devez également tout importer explicitement et vous devrez répéter encore plus de choses.)
  1. Après avoir lu la discussion #1170, je suis presque d'accord maintenant. Mais .. Je ne vois vraiment pas l'intérêt de supprimer le code existant qui fournissait cette fonctionnalité au lieu de simplement faire strict: true la valeur par défaut. Les goûts diffèrent, et certaines personnes préféreront avoir une approche "de repli", parfois plus intuitive. Surtout, lorsque les composants sont chargés dynamiquement. Cela peut être contourné avec des mixins, des usines, etc., mais tout cela prend un temps précieux.
  2. Chose sûre. Mais il y a toujours un équilibre entre "l'architecture idéale" et le coût des changements. Dans ce cas quelques lignes de code (à savoir : environ 10) celles qui ne feraient de mal à personne si elles restaient me coûtaient un temps non négligeable. Et je dois utiliser cette version instable car j'ai vraiment besoin de "filtres de liaison en lecture-écriture" qui ne seront probablement pas rétroportés vers 0.12.x
  3. Voir 2.
  4. La question n'est pas "quelle API est-ce que je préfère". Je préfère Vue. Point final. La question est "si je peux compter sur l'API Vue à long terme". La fiabilité plutôt que la beauté. Si les changements se cassent, il devrait y avoir une raison sérieuse pour eux. Dans le cas de la nouvelle syntaxe de liaison, qui a cassé TOUT mon code - d'accord, laissez-le être, c'est plus lisible et force une meilleure structure de code. Dans ce cas - non. Ce changement peut être ininterrompu avec options.strict = true défini par défaut.

Oui, la mise à niveau s'accompagne toujours de la douleur de la refactorisation, mais 1.0 est la seule chance pour Vue de se libérer de ces options de configuration héritées. Après la 1.0, ce sera strictement semver, et rien ne devrait casser jusqu'à la 2.0. Et je veux que la version 1.x dure le plus longtemps possible, à cause du problème de fiabilité dont vous avez parlé.

En ce qui concerne le mode strict : cela coûte sûrement du temps de refactorisation lorsque vous vous y êtes fortement appuyé - mais idéalement pour les nouveaux utilisateurs qui prennent Vue après 1.0, ils n'ont même pas besoin de savoir que cette chose existait. La surface de l'API doit être aussi petite que possible et le modèle de structuration global doit être aussi cohérent que possible. Permettre de désactiver le mode strict encourage essentiellement deux styles différents de structuration des applications Vue - imaginez que des personnes travaillent sur une application qui utilise strict: true , puis passent à un autre projet qui utilise strict: false ... cela crée fragmentation de l'expérience des développeurs, et je veux me débarrasser de cette possibilité, et 1.0 est le seul endroit raisonnable pour le faire.

C'est un peu malchanceux pour vous d'être pris au milieu de cette transition, et j'apprécie vos commentaires. Mais ce qui doit être fait doit être fait.

@ yyx990803 Je peux voir un cas d'utilisation concret avec lequel je suis un peu coincé.

Ce que j'essaie de faire

Je construis une application extensible : extensible avec des widgets. Un widget est un élément de l'application défini par le développeur qui se connecte à l'application globale pour l'étendre à certains points ; il est chargé dynamiquement au démarrage de l'application. Chaque instance de l'application peut avoir un ensemble différent de widgets et 2 instances de l'application peuvent vivre sur la même page.

Une fois chargé, le widget ajoutera un composant créé dynamiquement à l'application vuejs.
Les widgets peuvent contenir d'autres widgets (enfants). Nous ne savons pas comment ce sera au démarrage car cette partie est gérée par l'utilisateur après le chargement de l'application. C'est pourquoi les widgets doivent être conscients les uns des autres et enregistrer d'autres widgets.

Le problème

Je veux éviter d'enregistrer ces widgets globalement (raisons de compatibilité).
Étant donné que les composants sont créés et chargés dynamiquement, je dois enregistrer tous les composants de chaque composant pouvant contenir des enfants. Cela fait beaucoup d'inscriptions qui pourraient ne pas être utilisées. Voyez ce que je veux dire (veuillez noter que pour le moment je n'ai pas essayé l'enregistrement local des composants, en faisant les tests avec l'enregistrement global):

var components = {

    appComponents: {
       template: "...",
       components: components
    },

    appComponents2: {
       template: "...",
       components: components
    },

    widgetComponents: {
       template: "...",
       components: components
    },

    widgetComponents2: {
       template: "...",
       components: components
    },

}

Y a-t-il un goulot d'étranglement de performance lors de cette opération ?

C'est pourquoi je pense que la portée d'un composant "semi global" pourrait être utile. Cela pourrait aider à créer des applications avec une portée fermée de composants où les composants seront accessibles à partir des composants racine et enfants. Mais pas à partir d'autres racines vuejs. Qu'en penses-tu?

L'enregistrement global ne s'enregistre pas de manière récursive, il enregistre uniquement le
composant lui-même et non ceux à l'intérieur de son option components . donc je suppose
il n'y avait pas de problème.

Le lundi 22 août 2016, 16h00, Soufiane Ghzal [email protected] a écrit :

@yyx990803 https://github.com/yyx990803 Je peux voir un cas d'utilisation concret
Je suis un peu coincé avec.

_Ce que j'essaie de faire_

Je construis une application extensible : extensible avec des widgets. Un widget est un
partie de l'application définie par le développeur, elle est chargée dynamiquement au
démarrage de l'application. Chaque instance de l'application peut avoir
ensemble différent de widgets.

Une fois chargé, le widget ajoutera un composant créé dynamiquement au
application vuejs.
Les widgets peuvent contenir d'autres widgets (enfants)., nous ne savons pas comment il
sera au démarrage car cette partie est gérée par l'utilisateur après
l'application est chargée. C'est pourquoi les widgets doivent être conscients les uns des autres.

_Le problème_

Je veux éviter d'enregistrer ces widgets globalement.
Parce que les widgets sont chargés dynamiquement, je dois enregistrer tous les widgets dans
chaque widget pouvant contenir des enfants. Cela fait beaucoup d'enregistrement
pourrait ne pas être utilisé. Voir:

var composants = {

appComponents: {
   template: "...",
   components: components
},

appComponents2: {
   template: "...",
   components: components
},

}

_Y a-t-il un goulot d'étranglement au niveau des performances ?_


Vous recevez ceci parce que vous êtes abonné à ce fil.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/vuejs/vue/issues/1297#issuecomment -241339596, ou muet
le fil
https://github.com/notifications/unsubscribe-auth/AFTLl6QDePtH93VOU2lgrC72Z0vKLsv-ks5qiVcXgaJpZM4F7M1v
.

@fnlctrl Il ne s'agit pas d'un enregistrement mondial.

Le problème est que

  • registre d'enregistrement global pour toutes les instances de vue+component.
  • registres d'enregistrement locaux pour le composant actuel uniquement
  • et il n'y a aucun moyen de s'enregistrer pour root et les enfants "semi global": quelque chose qui permet de s'enregistrer pour l'instance de vue actuelle (y compris les composants ajoutés dans cette instance également).

À mon avis, le problème est que vue est conçu pour la manière statique (globale), mais il est limité pour la manière packagée/distribuable (locale).

L'enregistrement semi-global est en fait déjà possible puisque Vue possède un héritage prototypique.
https://jsfiddle.net/fnlCtrl/32dt9e9g/

@fnlctrl
Je ne suis pas sûr de comprendre ce que vous voulez montrer avec le violon que vous avez envoyé (veuillez noter que votre exemple comporte une erreur : Unknown custom element: <bar> - did you register the component correctly? )

Je ne suis pas assez clair, peut-être que vous n'avez pas compris ce que je veux expliquer. Recommençons:

Ce que je veux expliquer, c'est que nous pouvons :

  • enregistrez globalement les composants avec Vue.component('name', {...}) (c'est parfait pour une application d'une seule page)
  • Enregistrez localement un composant dans un composant new Vue({ components: {...} }); (c'est bien d'expédier des composants avec des dépendances pour une réutilisation locale)

Mais nous ne pouvons pas rendre les composants disponibles du parent aux enfants. Quelque chose comme enregistrer globalement les composants pour l'instance vm actuelle et tous les composants chargés dans cette instance, mais pas pour les composants chargés dans d'autres instances vm. Voir l'exemple : https://jsfiddle.net/p8wqafm1/2/

Comprenez vous?

Oups il semble que mon violon n'ait pas été enregistré correctement..
C'est celui que je comptais vous montrer..
https://jsfiddle.net/fnlCtrl/32dt9e9g/1/

Je lis votre exemple en ce moment.

J'ai fourché votre exemple ici qui est corrigé pour fonctionner, j'espère que je vous ai bien compris:
Vous souhaitez ajouter des composants dynamiques limités à l'intérieur de Foo.

@fnlctrl Merci pour votre exemple, mais il semble qu'il ne couvre pas encore ce que j'essaie de réaliser.

L'utilisation de la méthode dans votre exemple enregistre le composant dans Foo uniquement, mais cela ne les rend pas disponibles dans les enfants de Foo ( Bar dans cet exemple).

Voyez le violon, j'enregistre Baz en Foo et j'aimerais qu'il soit disponible en Bar car il est chargé depuis Foo : https://jsfiddle .net/8y0Lmb01/3/

Forker votre exemple : https://jsfiddle.net/fnlCtrl/uvzaotaz/

Le fait est que les composants doivent avoir une arborescence de dépendances claire et que les composants dynamiques dépendant les uns des autres ne doivent pas être une exception.

@fnlctrl Dans votre exemple, Baz n'est plus disponible dans Foo .

Pour ce faire, je pourrais utiliser Vue.component('baz', {...} mais le problème est qu'il "polluera" une autre instance de vue avec ce composant baz .

OU

Je pourrais enregistrer Baz dans les deux Foo et Bar , et tous les enfants foo, et tous les enfants de bar, et tous les grands enfants Foo, etc... Mais cela ajoute un beaucoup de complexité en cas d'application volumineuse/dynamique

Est-ce que tu vois ce que je veux dire? Je peux m'enregistrer localement, mais nous ne pouvons pas hériter des composants pour les enfants, petits-enfants,... des composants actuels _uniquement_

Oui, je vois votre point maintenant. Désolé de ne pas savoir que l'inscription
composant sous Foo ne le rend pas global dans la portée de Foo, contrairement à
Vue.composant . Va regarder dans la source pour voir pourquoi.

Le lundi 22 août 2016, 20:17 Soufiane Ghzal [email protected] a écrit :

@fnlctrl https://github.com/fnlctrl Dans votre exemple, Baz n'est pas disponible
dans Foo plus.

Pour ce faire, je pourrais utiliser Vue.component('baz', {...} mais le problème est que
cela "polluera" une autre instance de vue avec ce composant baz.

OU

Je pourrais enregistrer Baz dans Foo et Bar, et tous les enfants foo, et
tous les enfants du bar, et tous les Foo Grand Children, etc... Mais cela ajoute beaucoup de
complexité en cas d'application volumineuse/dynamique

Est-ce que tu vois ce que je veux dire? Je peux m'inscrire localement, mais nous ne pouvons pas hériter
composants pour les enfants, petits-enfants,... des composants actuels
_seul_


Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/vuejs/vue/issues/1297#issuecomment -241395119, ou muet
le fil
https://github.com/notifications/unsubscribe-auth/AFTLl2ud0GDO_hOwFN8GIA1TzVEF1q0Fks5qiZM9gaJpZM4F7M1v
.

Merci :)

Le fait est que je veux livrer une bibliothèque autonome qui dépend de vue, enregistrer des composants pour une instance donnée sera un réel avantage, car de toute façon, en tant que bibliothèque autonome, je ne suis pas autorisé à enregistrer cela dans l'instance globale de Vue ( cela casserait la partie autonome).

S'il vous plaît, faites-moi savoir si ce dont je parlais pourrait être implémenté dans Vue

Eh bien, je suppose que la raison était trop évidente pour que je l'ignore : je n'étais pas
en utilisant new Foo() ...

J'aurai un violon dans 20 minutes, lors de mon retour à la maison.

Le lundi 22 août 2016 à 20 h 27, 宋铄运[email protected] a écrit :

Oui, je vois votre point maintenant. Désolé de ne pas savoir que l'inscription
composant sous Foo ne le rend pas global dans la portée de Foo, contrairement à
Vue.composant . Va regarder dans la source pour voir pourquoi.

Le lun. 22 août 2016, 20:17 Soufiane Ghzal [email protected]
a écrit:

@fnlctrl https://github.com/fnlctrl Dans votre exemple, Baz n'est pas
disponible dans Foo plus.

Pour ce faire, je pourrais utiliser Vue.component('baz', {...} mais le problème est que
cela "polluera" une autre instance de vue avec ce composant baz.

OU

Je pourrais enregistrer Baz dans Foo et Bar, et tous les enfants foo, et
tous les enfants du bar, et tous les Foo Grand Children, etc... Mais cela ajoute beaucoup de
complexité en cas d'application volumineuse/dynamique

Est-ce que tu vois ce que je veux dire? Je peux m'inscrire localement, mais nous ne pouvons pas hériter
composants pour les enfants, petits-enfants,... des composants actuels
_seul_


Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/vuejs/vue/issues/1297#issuecomment -241395119, ou muet
le fil
https://github.com/notifications/unsubscribe-auth/AFTLl2ud0GDO_hOwFN8GIA1TzVEF1q0Fks5qiZM9gaJpZM4F7M1v
.

Eh bien, new Foo(...) n'a pas aussi bien fonctionné : https://jsfiddle.net/8y0Lmb01/5/

Etrange en effet... https://jsfiddle.net/fnlCtrl/p0ggkncu/
Regardant dedans maintenant.

J'ai lu une partie du code source et j'ai découvert que sur Vue lui-même, nous pouvons pirater comme ceci :
https://jsfiddle.net/fnlCtrl/522aw9sm/
(sans utiliser Vue.extend ou Vue.component, d'ailleurs Vue.component n'est qu'une fonction d'assistance qui fait Vue.extend et modifie Vue.options.components)

Bien que la même approche ne fonctionne pas sur une vue étendue :
https://jsfiddle.net/fnlCtrl/v1m2s16u/

Je suppose donc que le problème est causé par la résolution des composants. Je vais continuer à chercher.

@fnlctrl Ok merci, j'ai essayé de vérifier certaines choses et je suis arrivé à la même conclusion que la vôtre. Je ne connais pas assez le noyau pour comprendre pourquoi cela fonctionne de cette façon. Sait-on si c'est le comportement attendu ?

Je pense que ces commentaires sur resolveAsset

Résoudre un actif.
Cette fonction est utilisée car les instances enfants ont besoin d'un accès
aux actifs définis dans sa chaîne ancêtre.

suggère que l'enregistrement des composants sur Vue étendue est censé fonctionner.

Le code dans le corps de la fonction ne regarde pas la chaîne d'ancêtres, n'est-ce pas ? Peut-être n'a-t-il pas encore été implémenté ?

Je n'en sais pas encore assez, j'apprends encore son comportement, mais je suppose que la "chaîne d'ancêtres" fait référence au premier paramètre options .

Je suppose que je peux conclure que la cause est la suivante (src/core/global-api/extend) .
Cela oblige les classes étendues à utiliser la même méthode que leurs parents.

J'ai testé cela, si vous copiez ce qui se trouve dans core/global-api/assets (utilisez le code correspondant dans la version dist qui est dépourvue de types, bien sûr)
à vue.extend, pour le faire ressembler à ceci (changez Vue en Sub ):

config._assetTypes.forEach(function (type) {
        Sub[type] = function (id, definition) {
          if (!definition) {
            return this.options[type + 's'][id];
          } else {
            /* istanbul ignore if */
            if ("development" !== 'production') {
              if (type === 'component' && config.isReservedTag(id)) {
                warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
              }
            }
            if (type === 'component' && isPlainObject(definition)) {
              definition.name = definition.name || id;
              definition = Sub.extend(definition);
            }
            if (type === 'directive' && typeof definition === 'function') {
              definition = { bind: definition, update: definition };
            }
            this.options[type + 's'][id] = definition;
            return definition;
          }
        };
      });

les Foo = Vue.extend() et Foo.component() fonctionneront.

Bien que je suppose que cela entraînera une pénalité de performance.

@gsouf Et je pense avoir trouvé la dernière pièce du puzzle (une solution de contournement équivalente sans modifier la vue):
https://jsfiddle.net/fnlCtrl/v1m2s16u/

var Root = Vue.extend()

Root.options.components.Foo = Root.extend({
    template: '<div>Foo</div>'
})

Root.options.components.Bar = Root.extend({
    template: '<div>Bar, uses <foo></foo></div>'
})

new Root({
    template: `
  <div>
    <foo></foo>
    <bar></bar>
  </div>
  `
}).$mount('#app')

Salut @fnlctrl , merci pour la sortie et désolé pour le retard.

En effet, on dirait que les composants héritent des composants de leur constructeur, pas de leur parent. Je cherche actuellement si je peux appliquer un correctif pour mon cas d'utilisation.

Dans votre cas, il reste attaché à un constructeur, pas à une instance, je cherche à ce qu'il fasse partie de l'instance uniquement

@fnlctrl en raison du fonctionnement de javascript et grâce à votre exemple, je pourrais le contourner en "étendant dynamiquement" la vue pour chaque instance que je crée, rendant tout disponible pour cette application uniquement.:

createVueInstance = function(el, data){
    var vExtend = Vue.extend();
    vExtend.partial('some-semiglobal-partial', "...");
    vExtend.component('some-semiglobal-component', vExtend.extend({...}));

    return new vExtend({
        el: el,
        data: data
    });
};

Après avoir vérifié comment le noyau a été construit, il ne semble pas qu'il ait été construit pour permettre une intégration facile des composants disponibles par instance et cette solution de contournement est suffisamment stable pour moi.

Merci pour ton aide!

Au fait, je pense que l'exemple que vous m'avez montré pourrait être expliqué en profondeur dans la doc. Je n'ai pas trouvé de mention à ce sujet

@gsouf De rien . Je pense que ce problème est suffisant pour ceux qui souhaitent implémenter une fonctionnalité similaire :smile:

Ici, j'ai un cas d'utilisation "semi-global":

J'ai un composant Layout relativement universel, mais le contenu est configurable par les composants qui utilisent le composant Layout, par exemple. composant A utilise Layout et souhaite configurer son contenu avec le composant B, un autre composant peut utiliser Layout et configurer son contenu avec le composant C, etc.

Ce modèle doit-il être pris en charge ?

Ou existe-t-il une solution pour remplacer cette conception?

Le modèle est largement utilisé dans iOS pour améliorer la réutilisation du code et il s'agit d'une conception flexible.

@hpsoar Ce dont vous avez probablement besoin, ce sont des machines à sous

Ma conception est la suivante, en gros, cela me permettra de faire deux choses avec la cellule :

  1. config simple de la cellule avec un style css, qui suffira dans bien des cas ;
  2. insérer un composant dans la cellule, qui sera utilisé pour des cas particuliers.
<template>
  <div class="tile is-ancestor">
    <div class="tile is-parent">
      <article class="tile is-child box">
        <div class="table-responsive">
          <table class="table is-bordered is-striped is-narrow">
            <thead>
            <tr>
              <th v-for="c in columns">
                {{c.title}}
              </th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(item, index) in items">
              <template v-for="c in columns">
                <td v-if="c.hasOwnProperty('component')"><div :is="c.component"></div></td>
                <td v-else>{{ item[c.name] }}</td>
              </template>
            </tr>
            </tbody>
          </table>
        </div>
      </article>
    </div>
  </div>
</template>

<script>

export default {
  components: {
  },
  props: [
    'columns',
    'items'
  ],
  data: function () {
    return {
    }
  }
}

</script>

<style lang="scss" rel="stylesheet/scss">
  .table-responsive {
    display: block;
    width: 100%;
    min-height: .01%;
    overflow-x: auto;
  }
</style>

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