Características de Vue para recordar

Nosotros, en el equipo de Ptah , decidimos ir un poco más allá del SPA habitual e intentamos usar a Vue como diseñador de páginas de destino. Y ahora queremos compartir parte de nuestra experiencia.

Este artículo es principalmente para aquellos que acaban de comenzar a trabajar con Vue y desean conocer mejor sus características y capacidades. Aquí quiero hablar sobre algunas de las características del marco, que a menudo permanecen inmerecidamente olvidadas por los desarrolladores novatos.

Funciones de render


Las plantillas de componentes son una de las cosas por las que los desarrolladores adoran Vue. Son simples y lógicos, gracias a ellos el marco tiene un umbral de entrada bajo. La sintaxis de la plantilla es suficiente en el 90% de los casos para escribir código lógico y hermoso. Pero, ¿qué hacer si está en el 10% restante y no puede escribir un componente compacto? La función de renderizado te ayudará.

Veamos qué está usando el ejemplo de la documentación:

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

El componente de encabezado anclado acepta la propiedad de nivel y dibuja la etiqueta de título. Grabando así

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

Se convertirá a

<h1>, !</h1>

Si este componente se describiera utilizando una plantilla estándar, contendría hasta 6 v-if condiciones que describen diferentes niveles de encabezados:

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

¿Cómo funciona?


El método de procesamiento toma dos argumentos. El primer argumento para createElement es una función que describe qué elemento Vue debe crear. En la comunidad, se acostumbra abreviar createElement a una letra - h . El segundo argumento es el contexto , para acceder a los datos del contexto.

createElement toma tres argumentos:

  1. El artículo a crear. Esto puede ser no solo una etiqueta HTML, sino también un nombre de componente. Este argumento es obligatorio;
  2. Un objeto con datos. Puede contener una lista de clases, estilos, parámetros de entrada para el componente, métodos para manejar eventos, etc. Para más detalles, consulte la documentación. Argumento opcional;
  3. Nodos virtuales secundarios. Puede ser una cadena o una matriz. En el ejemplo anterior, esto es esto. $ Slots.default .

Las funciones de procesamiento pueden ayudar en las situaciones más inesperadas. Por ejemplo, en Ptah, a menudo necesitamos usar la etiqueta de estilo dentro de la página para que algunos elementos del diseñador funcionen correctamente. Sin embargo, Vue prohíbe el uso de esta etiqueta dentro del componente de plantilla . Esta limitación se evita fácilmente gracias a un pequeño contenedor:

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

Ahora dentro de las plantillas, en lugar de la etiqueta de estilo , puede usar v-style .

Por lo tanto, tan pronto como le parezca que las características estándar de las plantillas de Vue no son suficientes, piense en las funciones de Render. Parecen complicados solo a primera vista, pero puede usar todas las características que JS proporciona en ellos.

Mixins


¿Tiene varios componentes similares con código duplicado? Las mixinas o impurezas ayudarán a cumplir con el principio DRY: la funcionalidad de las mixinas puede usarse en varios componentes a la vez.

Pongamos un ejemplo. Digamos que tenemos 2 componentes con una lógica similar:

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

Los componentes son diferentes, pero tienen la misma lógica. Para hacerlo, deberá crear un archivo js normal. Sería lógico colocarlo en el directorio mixins junto a los componentes.

// 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'
    }
  }
}

Como puede ver en el ejemplo, casi toda la lógica migró al mixin. Cuando se usan impurezas dentro de los componentes, todas sus opciones se fusionan. Y en el componente, puede llamar libremente al método initEditor () y, por el contrario, en la impureza, se usa elementName del componente aquí . En este caso, los objetos de datos se fusionarán de forma recursiva y las propiedades del componente tendrán prioridad.

Por lo tanto, los beneficios de las impurezas son obvios: esta es la reutilización del código. Pero hay un menos. Este ejemplo es sintético, solo un par de líneas. Los componentes reales, como los utilizados en Ptah, se pueden escribir en un par de cientos de líneas de código. No siempre será claro para una persona que no ha escrito este código cómo funciona, especialmente si pasa por alto la adición de mixins al componente. Desafortunadamente, deshacerse de este menos no funcionará. Puedo recomendar dos cosas: describir el funcionamiento del componente en JSDoc y usar nombres especiales para las propiedades de la impureza (por ejemplo, puede agregar un prefijo, que estará de acuerdo con el equipo de antemano).

Proporcionar / inyectar


Este par de opciones siempre se usan juntas y le permite transferir datos desde el componente principal a toda la jerarquía de sus descendientes. Estas opciones se utilizan principalmente para escribir complementos; la documentación oficial no recomienda su uso en aplicaciones. En aplicaciones, la comunicación entre componentes se construye muy bien en Vuex. Sin embargo, esta funcionalidad aún merece atención.

¿Cómo funciona?


Primero, necesitamos definir los datos en el componente padre, que pasaremos a sus descendientes.

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

Ahora los datos transferidos deben integrarse en el componente secundario.

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

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

Como puede ver en el ejemplo, todo es bastante simple. Sin embargo, se debe tener en cuenta una desventaja significativa: ¡los datos del paquete de suministro / inyección no son reactivos por defecto! Sin embargo, este inconveniente se evita fácilmente con Object.defineProperty :

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

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

Ahora, cambiar este dispositivo en el padre lo cambiará en los descendientes.

Meta componente


Hay situaciones en las que no se sabe de antemano qué componente se utilizará en el código. Considere un ejemplo de nuestro editor. La tarea es la siguiente: en la sección condicional de FirstScreen, muestre los elementos Texto, Logotipo, Botón , luego agregue SocialIcons a estos elementos .

Por lo tanto, es obvio que tendremos un componente de sección que servirá como contenedor para los elementos y 4 componentes para los elementos mismos. La estructura será aproximadamente la siguiente:

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

Agregar todos los componentes de los elementos a la plantilla FirstScreen a la vez y luego cambiarlos usando las condiciones sería una decisión extremadamente imprudente. Hay una herramienta simple y maravillosa para tales tareas:

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

El elemento componente con el atributo : es , que simplemente escribe el nombre del componente. Y gracias a él, nuestra tarea simplemente se resuelve:

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

En la matriz de elementos , escribimos los nombres de los componentes y luego simplemente mostramos estos componentes en un bucle dentro de la plantilla FirstScreen . Ahora, para agregar un elemento con íconos de redes sociales a nuestra sección, solo necesitamos ejecutar this.elements.push ('SocialIcons') .

All Articles