Introducción a Vue.js: componentes, accesorios y ranuras | Programar Plus

Esta es la segunda parte de una serie de cinco partes sobre el marco de JavaScript, Vue.js. En esta parte, repasaremos Componentes, Props y Slots. Esta no pretende ser una guía completa, sino más bien una descripción general de los conceptos básicos para que pueda comenzar a utilizar Vue.js y comprender lo que el marco tiene para ofrecer.

Serie de artículos:

  1. Representación, directivas y eventos
  2. Componentes, accesorios y ranuras (¡Estás aquí!)
  3. Vue-cli
  4. Vuex
  5. Animaciones

Componentes y transferencia de datos

Si está familiarizado con React o Angular2, la idea de componentes y estado de paso no será nueva para usted. En caso de que no lo esté, repasemos algunos de los conceptos principales.

Los sitios web grandes y pequeños generalmente se componen de diferentes partes, y abstraerlos en partes más pequeñas hace que sean fáciles de estructurar, razonar, reutilizar y hacer que nuestro código sea más legible. En lugar de examinar todo el marcado en una página larga y multifacética, podríamos incluir componentes como este:

<header></header>
<aside>
  <sidebar-item v-for="item in items"></sidebar-item>
</aside>
<main>
  <blogpost v-for="post in posts"></blogpost>
</main>
<footer></footer>

Este es un ejemplo simplificado, pero puede ver cuán útil puede ser este tipo de composición a medida que comienza a construir la estructura de su sitio. Si tuviera que sumergirse en este código como mantenedor, no se necesitaría mucho para comprender cómo está estructurada la aplicación o dónde buscar cada pieza.

Vue nos permite crear componentes de diferentes formas. Trabajemos de lo simple a lo complejo, teniendo en cuenta que el ejemplo complejo es más fiel a la forma en que se vería una aplicación Vue promedio.

new Vue({
  el: 'hello',
  template: '<h1>Hello World!</h1>'
});

Esto funciona, pero no es muy útil, ya que solo se puede usar una vez y aún no estamos pasando la información a diferentes componentes. Una forma de pasar datos de un padre a un hijo se llama accesorios.

Este es un ejemplo tan simple como pude hacer, por lo que es súper claro. Recuerda que el :text en el HTML es un atajo para el enlace de Vue. Cubrimos esta última vez en la sección de directivas. El enlace se puede usar para todo tipo de cosas, pero en este caso, evita que tengamos que colocar el estado en una plantilla de bigote, como esta. {{ message }}.

En el código siguiente, Vue.component es el componente, y new Vue se llama el ejemplo. Puede tener más de una instancia en una aplicación. Normalmente, tendremos una instancia y varios componentes, ya que la instancia es la aplicación principal.

Vue.component('child', {
  props: ['text'],
  template: `<div>{{ text }}<div>`
});

new Vue({
  el: "#app",
  data() {
    return {
      message: 'hello mr. magoo'
    }
  }
});
<div id="app">
  <child :text="message"></child>
</div>

Ver la pluma.

Ahora podemos reutilizar este componente tantas veces como queramos a través de nuestra aplicación:

<div id="app">
  <child :text="message"></child>
  <child :text="message"></child>
</div>

Ver la pluma.

También podemos agregar validación a nuestros accesorios, que es similar a PropTypes en React. Esto es bueno porque se autodocumenta y devolverá un error si no es lo que esperábamos, pero solo en el modo de desarrollo:

Vue.component('child', {
  props: {
    text: {
      type: String,
      required: true
    }
  },
  template: `<div>{{ text }}<div>`
});

En el siguiente ejemplo, estoy cargando Vue en modo de desarrollo y paso a propósito un tipo no válido a nuestra validación de prop. Puedes ver el error en la consola. (También le permite saber que puede usar las herramientas de desarrollo de Vue y dónde encontrarlas).

Vue.component('child', {
  props: {
    text: {
      type: Boolean,
      required: true
    }
  },
  template: `<div>{{ text }}<div>`
});

Vea los accesorios simples de Pen con validación de Sarah Drasner (@sdras) en CodePen.

Los objetos deben devolverse como una función de fábrica e incluso puede pasar como una función de validación personalizada, lo cual es realmente bueno porque puede verificar los valores con el negocio, la entrada u otra lógica. Hay un buen resumen de cómo usaría cada tipo en la guía aquí.

No necesita pasar necesariamente los datos en accesorios al niño, tampoco tiene la opción de usar el estado o un valor estático como mejor le parezca:

Vue.component('child', {
  props: { 
    count: {
      type: Number,
      required: true
    }
  },
  template: `<div class="num">{{ count }}</div>`
})

new Vue({
  el: '#app',
  data() {
    return {
      count: 0    
    }
  },
  methods: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    }
  }
})
<div id="app">
  <h3>
    <button @click="increment">+</button>
    Adjust the state
    <button @click="decrement">-</button>
  </h3>
  <h2>This is the app state: <span class="num">{{ count }}</span></h2>
  <hr>
  <h4><child count="1"></child></h4> 
  <p>This is a child counter that is using a static integer as props</p>
  <hr>
  <h4><child :count="count"></child></h4>
  <p>This is the same child counter and it is using the state as props</p>
</div>

Vea el componente secundario de Pen usando y no usando accesorios de Sarah Drasner (@sdras) en CodePen.

La diferencia es si está pasando o no una propiedad y vinculandola:

No usar el estado
<child count="1"></child>

vs

Usando el estado
<child :count="count"></child>

Hasta ahora, hemos estado creando contenido en nuestro componente secundario con una cadena y, por supuesto, si está usando babel para poder procesar ES6 en todos los navegadores (lo que recomiendo encarecidamente), podría usar una plantilla literal para evite la concatenación de cadenas potencialmente difíciles de leer:

Vue.component('individual-comment', {
  template: 
  `<li> {{ commentpost }} </li>`,
  props: ['commentpost']
});

new Vue({
  el: '#app',
  data: {
    newComment: '',
    comments: [
      'Looks great Julianne!',
      'I love the sea',
      'Where are you at?'
    ]
  },
  methods: {
    addComment: function () {
      this.comments.push(this.newComment)
      this.newComment=""
    }
  }
});
<ul>
    <li
      is="individual-comment"
      v-for="comment in comments"
      v-bind:commentpost="comment"
    ></li>
  </ul>
  <input
    v-model="newComment"
    v-on:keyup.enter="addComment"
    placeholder="Add a comment"
  >

Vea el Pen cd81de1463229a9612dca7559dd666e0 de Sarah Drasner (@sdras) en CodePen.

Esto es un poco más útil, pero todavía hay un límite a la cantidad de contenido que probablemente queremos poner en esa cadena, incluso con la ayuda de literales de plantilla. Eventualmente, en este formulario de comentarios, querríamos tener fotos y los nombres de los autores, y probablemente ya puedas adivinar cuán lleno estaría con toda esa información. Tampoco tendremos ningún resaltado de sintaxis útil dentro de esa cadena.

Con todas esas cosas en mente, creemos una plantilla. Envolveremos algo de HTML normal en etiquetas de script especiales y usaremos una identificación para hacer referencia a él para crear un componente. Puedes ver que esto es mucho más legible cuando tenemos mucho texto y elementos:

<!-- This is the Individual Comment Component -->
<script type="text/x-template" id="comment-template">
<li> 
  <img class="post-img" :src="https://css-tricks.com/intro-to-vue-2-components-props-slots/commentpost.authorImg" /> 
  <small>{{ commentpost.author }}</small>
  <p class="post-comment">"{{ commentpost.text }}"</p>
</li>
</script>
Vue.component('individual-comment', {
  template: '#comment-template',
  props: ['commentpost']
});

Vea la publicación de la aplicación Pen Photo con Vue.js de Sarah Drasner (@sdras) en CodePen.

Ranuras

Esto es mucho mejor. Pero, ¿qué sucede cuando tenemos dos componentes con ligeras variaciones, ya sea de contenido o de estilo? Podríamos pasar todos los diferentes contenidos y estilos al componente con accesorios y cambiar todo cada vez, o podríamos bifurcar los componentes y crear diferentes versiones de ellos. Pero sería muy bueno si pudiéramos reutilizar los componentes y completarlos con los mismos datos o funcionalidad. Aquí es donde las tragamonedas son realmente útiles.

Digamos que tenemos una instancia de aplicación principal usando el mismo <app-child> componente dos veces. Dentro de cada niño queremos algo del mismo contenido y contenido diferente. Para el contenido que queremos mantenernos consistentes, usaríamos una etiqueta p estándar, y para el contenido que queremos cambiar, pondremos un <slot></slot> etiqueta.

<script type="text/x-template" id="childarea">
  <div class="child">
    <slot></slot>
    <p>It's a veritable slot machine!<br> 
    Ha ha aw</p>
  </div>
</script>

Luego, en la instancia de la aplicación, podemos pasar contenido dentro del <app-child> etiquetas de componentes y automáticamente llenará las ranuras:

<div id="app">
  <h2>We can use slots to populate content</h2>
  <app-child>
    <h3>This is slot number one</h3>
  </app-child>
  <app-child>
    <h3>This is slot number two</h3>
    <small>I can put more info in, too!</small>
  </app-child>
</div>

Ver la pluma.

También puede tener contenido predeterminado dentro de las ranuras. Si, en la propia ranura, en lugar de escribir <slot></slot>, puede completarlo con:

<slot>I am some default text</slot>

Ese texto predeterminado se usará hasta que llene el espacio con otro material, ¡lo cual es muy útil! Choca los cinco por todos lados.

También puede tener ranuras con nombre. Si tuviera dos ranuras en un componente, podría diferenciarlas agregando un atributo de nombre <slot name="headerinfo"></slot> y podríamos acceder a esa ranura en particular escribiendo <h1 slot="headerinfo">I will populate the headerinfo slot!</h1>. Esto es muy útil. Si tiene varias ranuras con nombre y una que no, Vue colocará el contenido con nombre en las ranuras con nombre, y lo que quede se usará para llenar las ranuras sin nombre restantes.

He aquí un ejemplo de lo que quiero decir:

Esta es una plantilla secundaria de muestra

<div id="post">
  <main>
    <slot name="header"></slot>
    <slot></slot>
  </main>
</div>

Esta es una muestra del padre

<app-post>
  <h1 slot="header">This is the main title</h1>
  <p>I will go in the unnamed slot!</p>
</app-post>

Contenido renderizado

<main>
  <h1>This is the main title</h1>
  <p>I will go in the unnamed slot!</p>
</main>

Personalmente, si estoy usando más de una ranura a la vez, nombraré todas para que quede muy claro qué va a hacer para otros mantenedores, pero es bueno que Vue proporcione una API tan flexible.

Ejemplo de tragamonedas

Alternativamente, podemos tener estilos particulares asignados para diferentes componentes y mantener todo el contenido dentro del mismo, por lo tanto, cambiar rápida y fácilmente la apariencia de algo. En la etiquetadora de vino a continuación, uno de los botones alternará el componente y el color en función de lo que seleccione el usuario, y el fondo de la botella, la etiqueta y el texto cambiarán, manteniendo el contenido estable.

const app = new Vue({
  ...
  components: {
    'appBlack': {
      template: '#black'
    }
  }
});

HTML de la aplicación principal de Vue:

  <component :is="selected">
    ...
    <path class="label" d="M12,295.9s56.5,5,137.6,0V409S78.1,423.6,12,409Z" transform="translate(-12 -13.8)" :style="{ fill: labelColor }"/>
    ...
  </component>

<h4>Color</h4>
  <button @click="selected ='appBlack', labelColor="#000000"">Black Label</button>
  <button @click="selected ='appWhite', labelColor="#ffffff"">White Label</button>
  <input type="color" v-model="labelColor" defaultValue="#ff0000">

HTML de componente blanco:

<script type="text/x-template" id="white">
  <div class="white">
     <slot></slot>
  </div>
</script>

(Esta es una demostración más grande, por lo que podría tener más sentido si juegas con ella en una ventana / pestaña separada)

Ver la pluma.

Cambio de etiqueta de vino

Ahora, estamos colocando todos los datos de la imagen SVG en la aplicación principal, pero en realidad está dentro de la <slot> en cada componente. Esto nos permite cambiar partes de contenido o diseñar cosas de manera diferente según el uso, lo cual es una característica realmente agradable. Puede ver que permitimos que el usuario decida qué componente utilizará mediante la creación de un botón que cambia el valor “seleccionado” del componente.

En este momento tenemos todo en una ranura, pero también podríamos usar varias ranuras y diferenciarlas mediante el nombre si quisiéramos:

<!-- main vue app instance -->
<app-comment>
  <p slot="comment">{{ comment.text }}</p>
</app-comment>

<!-- individual component -->
<script type="text/x-template" id="comment-template">
  <div>
    <slot name="comment"></slot>
  </div>
</script>

Podemos cambiar fácilmente entre diferentes componentes con las mismas ranuras referenciadas, pero ¿qué sucede cuando queremos poder cambiar de un lado a otro, pero nos aferramos al estado individual de cada componente? Actualmente, cuando cambiamos entre blanco y negro, las plantillas cambian y el contenido permanece igual. Pero tal vez tengamos una situación en la que queremos que la etiqueta negra sea completamente diferente a la etiqueta blanca. Hay un componente especial en el que puedes envolverlo llamado <keep-alive></keep-alive> que retendrá el estado a medida que cambie.

Vea esta desviación del ejemplo anterior: cree una etiqueta negra y luego una etiqueta blanca diferente y cambie entre ellas. Verás que el estado de cada uno se conserva, y son diferentes entre sí:

<keep-alive>
  <component :is="selected">
    ...
  </component>
</keep-alive>

Demostración Keep-Alive de etiqueta de vino

Vea Pen Vue Wine Label Maker, con keep-alive de Sarah Drasner (@sdras) en CodePen.

Me encanta esta función de la API.

Todo esto está bien, pero en aras de la simplicidad, hemos estado pegando todo en uno o dos archivos. Estaría mucho mejor organizado mientras construimos nuestro sitio si pudiéramos separar los componentes en diferentes archivos e importarlos cuando los necesitemos, y realmente así es como se realiza típicamente el desarrollo real en Vue, así que repasemos eso a continuación. ¡Sintonice la siguiente parte cuando hablemos de Vue-cli, procesos de compilación y Vuex para la gestión del estado!

Serie de artículos:

  1. Representación, directivas y eventos
  2. Componentes, accesorios y ranuras (¡Estás aquí!)
  3. Vue-cli
  4. Vuex
  5. Animaciones
(Visited 7 times, 1 visits today)