Caractéristiques à retenir

Nous, à l'équipe de Ptah, avons décidé d'aller un peu plus loin que le SPA habituel et avons essayé d'utiliser Vue comme concepteur de page de destination. Et maintenant, nous voulons partager une partie de notre expérience.

Cet article est principalement destiné à ceux qui viennent de commencer à travailler avec Vue et qui souhaitent mieux connaître ses fonctionnalités et capacités. Ici, je veux parler de certaines des fonctionnalités du framework, qui restent souvent injustement méritées par les développeurs novices.

Fonctions de rendu


Les modèles de composants sont l'une des choses pour lesquelles les développeurs aiment Vue. Ils sont simples et logiques, grâce à eux le framework a un seuil d'entrée bas. La syntaxe des modèles suffit dans 90% des cas pour écrire du code logique et beau. Mais que faire si vous êtes dans les 10% restants et que vous ne pouvez pas écrire un composant compact? La fonction de rendu vous aidera.

Voyons ce que c'est en utilisant l'exemple de la documentation:

Vue.component('anchored-heading', {
 render: function (createElement) {
   return createElement(
     'h' + this.level,   //  
     this.$slots.default //   
   )
 },
 props: {
   level: {
     type: Number,
     required: true
   }
 }
})

Le composant en -tête ancré accepte la propriété level et dessine la balise title. Ainsi l'enregistrement

<anchored-heading :level="1">, !</anchored-heading>

Sera converti en

<h1>, !</h1>

Si ce composant était décrit à l'aide d'un modèle standard, il contiendrait jusqu'à 6 conditions V-if qui décrivent différents niveaux d'en-têtes:

<h1 v-if="level === 1">
 <slot></slot>
</h1>
<h2 v-if="level === 2">
 <slot></slot>
</h2>
<h3 v-if="level === 3">
 <slot></slot>
</h3>
<h4 v-if="level === 4">
 <slot></slot>
</h4>
<h5 v-if="level === 5">
 <slot></slot>
</h5>
<h6 v-if="level === 6">
 <slot></slot>
</h6>

Comment ça fonctionne?


La méthode de rendu prend deux arguments. Le premier argument de createElement est une fonction qui décrit quel élément Vue doit créer. Dans la communauté, il est habituel d'abréger createElement à une lettre - h . Le deuxième argument est le contexte , pour accéder aux données de contexte.

createElement prend trois arguments:

  1. L'élément à créer. Il peut s'agir non seulement d'une balise HTML, mais aussi d'un nom de composant. Cet argument est requis;
  2. Un objet avec des données. Il peut contenir une liste de classes, de styles, de paramètres d'entrée pour le composant, de méthodes de gestion des événements, etc. Pour plus de détails, consultez la documentation. Argument facultatif;
  3. Nœuds virtuels enfants. Il peut s'agir d'une chaîne ou d'un tableau. Dans l'exemple ci-dessus, voici ceci. $ Slots.default .

Les fonctions de rendu peuvent aider dans les situations les plus inattendues. Par exemple, dans Ptah, nous devons souvent utiliser la balise de style à l'intérieur de la page pour que certains éléments du concepteur fonctionnent correctement. Cependant, Vue interdit l'utilisation de cette balise à l'intérieur du composant de modèle . Cette limitation est facilement contournée grâce à un petit wrapper:

Vue.component('v-style', {
 render: function (h) {
   return h('style', this.$slots.default)
 }
})

Maintenant, à l'intérieur des modèles, au lieu de la balise de style , vous pouvez utiliser le style v .

Donc, dès qu'il vous semble que les fonctionnalités standard des modèles Vue ne suffisent pas - pensez aux fonctions de rendu. Ils ne semblent compliqués qu'à première vue, mais vous pouvez utiliser toutes les fonctionnalités que JS propose.

Mixins


Avez-vous plusieurs composants similaires avec du code en double? Les mixines ou les impuretés aideront à respecter le principe DRY - la fonctionnalité des mixines peut être utilisée dans plusieurs composants à la fois.

Prenons un exemple. Disons que nous avons 2 composants avec une logique similaire:

export default  {
  name: 'TextElement',

  data () {
    return {
      elementName: 'Text',
      showEditor: false,
      editor: null
    }
  },
  
  methods: {
    initEditor () {
      this.showEditor = true
      this.editor = new Editor(this.elementName)
    }
  }
}

export default  {
  name: 'ButtonElement',

  data () {
    return {
      elementName: 'Button',
      showEditor: false,
      editor: null
    }
  },
  
  methods: {
    initEditor () {
      this.showEditor = true
      this.editor = new Editor(this.elementName)
    }
  }
}

Les composants sont différents, mais ont la même logique. Pour ce faire, vous devrez créer un fichier js normal. Il serait logique de le placer dans le répertoire mixins à côté des composants.

// mixin.js
export default  {
  data () {
    return {
      showEditor: false,
      editor: null
    }
  },
  
  methods: {
    initEditor () {
      this.showEditor = true
      this.editor = new Editor(this.elementName)
    }
  }
}

// TextElement.vue
import mixin from './mixins/mixin'

export default  {
  name: 'TextElement',

  mixins: [mixin]  //  

  data () {
    return {
      elementName: 'Text',
    }
  },
}

// ButtonElement.vue
import mixin from './mixins/mixin'

export default  {
  name: 'ButtonElement',

  mixins: [mixin]

  data () {
    return {
      elementName: 'Button'
    }
  }
}

Comme vous pouvez le voir dans l'exemple, presque toute la logique a migré vers le mixage. Lors de l'utilisation d'impuretés à l'intérieur des composants, toutes leurs options fusionnent. Et dans le composant, vous pouvez appeler librement la méthode initEditor () , et, inversement, dans l'impureté, elementName du composant est utilisé ici . Dans ce cas, les objets de données seront fusionnés récursivement et les propriétés du composant seront prioritaires.

Ainsi, les avantages des impuretés sont évidents - c'est la réutilisation du code. Mais il y a un moins. Cet exemple est synthétique, juste quelques lignes. Les composants réels, tels que ceux utilisés dans Ptah, peuvent être écrits en quelques centaines de lignes de code. Il ne sera pas toujours clair pour une personne qui n'a pas écrit ce code comment cela fonctionne, surtout s'il oublie d'ajouter des mixins au composant. Malheureusement, se débarrasser de ce moins ne fonctionnera pas. Je peux recommander deux choses: décrire le fonctionnement du composant dans JSDoc et utiliser des noms spéciaux pour les propriétés de l'impureté (par exemple, vous pouvez ajouter un préfixe, que vous conviendrez avec l'équipe à l'avance).

Fournir / Injecter


Cette paire d'options est toujours utilisée ensemble et vous permet de transférer des données du composant parent vers la hiérarchie entière de ses descendants. Ces options sont principalement utilisées pour écrire des plugins, la documentation officielle ne recommande pas de les utiliser dans les applications. Dans les applications, la communication entre les composants repose très bien sur Vuex. Cependant, cette fonctionnalité mérite toujours l'attention.

Comment ça fonctionne?


Tout d'abord, nous devons définir les données dans le composant parent, que nous transmettrons à ses descendants.

// Parent.vue        
export default {
  provide: {
   device: 'is-desktop'
  }
}

Maintenant, les données transférées doivent être incorporées dans le composant enfant.

// Child.vue    
export default {
  inject: ['device'],

  created () {
   console.log(this.device) // => "is-desktop"
  }
}

Comme vous pouvez le voir sur l'exemple, tout est assez simple. Mais un inconvénient important doit être noté - les données du bundle fournir / injecter ne sont pas réactives par défaut! Cependant, cet inconvénient est facilement contourné en utilisant Object.defineProperty :

provide () {
 let device = {}
 Object.defineProperty(device, 'type', { enumerable: true, get: () => this.device })
 return { device }
},

data () {
 return {
   device: 'is-desktop'
 }
}

Maintenant, changer this.device dans le parent le changera dans les descendants.

Composant Meta


Dans certains cas, on ne sait pas à l'avance quel composant sera utilisé dans le code. Prenons un exemple de notre éditeur. La tâche est la suivante: dans la section conditionnelle de FirstScreen, affichez les éléments Text, Logo, Button , puis ajoutez SocialIcons à ces éléments .

Il est donc évident que nous aurons un composant de section qui servira de conteneur pour les éléments et 4 composants pour les éléments eux-mêmes. La structure sera approximativement la suivante:

/ sections
 -- FirstScreen.vue
/ elements
 -- Text.vue
 -- Logo.vue
 -- Button.vue
 -- SocialIcons.vue

Ajouter tous les composants d'éléments au modèle FirstScreen à la fois, puis les changer en utilisant des conditions serait une décision extrêmement imprudente. Il existe un outil simple et merveilleux pour de telles tâches:

<component :is="%componentName%"/>

L'élément composant avec l'attribut : est dans lequel le nom du composant est simplement écrit. Et grâce à lui, notre tâche est simplement résolue:

<script>
export default  {
  name: 'FirstScreen',

  data () {
    return {
      elements: [
        'Text',
        'Logo',
        'Button',
      ],
    }
  }
}
</script>

<template>
  <div class="first-screen">
    <component v-for="element in elements" :is="element"/>
  </div>
</template>

Dans le tableau des éléments , nous avons écrit les noms des composants, puis simplement sorti ces composants dans une boucle à l'intérieur du modèle FirstScreen . Maintenant, afin d'ajouter un élément avec des icônes de réseaux sociaux à notre section, il nous suffit d'exécuter this.elements.push ('SocialIcons') .

All Articles