vue js (3): componentes básicos

Introducción a los componentes de vue js

Nicholas Krushenick

archivado en: JavaScript / 30 Marzo, 2017 / taller:

En las dos entradas anteriores de este taller dedicado a vue.js hemos trabajado directamente contra el DOM a partir de instancias; sin embargo, la manera apropiada de trabajar con este framework es mediante «componentes», un paradigma de programación web que siguen otras herramientas, como las versiones de angular superiores a la 1.5, polymer o react, inspiradas en mayor o menor medida en los web components nativos.

En esencia, un componente web debería ser una pieza de código que contenga en sí misma todas las instrucciones html, css y js necesarias para que un elemento funcione correctamente. Conseguir esto en vue.js podría parecer algo laborioso si no conocemos webpack, pero una vez que se aprenden cuatro conceptos básicos resulta muy sencillo y elegante. Vamos, por lo tanto, paso a paso. Primero veremos cómo funcionan los componentes más sencillos y en la próxima entrada los más complejos.

Componentes globales y locales

En vue.js, definir un componente básico es tan fácil como «registrarlo» mediante el método component(), el cual recibe dos parámetros: el nombre del componente, que se recomienda que sea compuesto para diferenciarlo de los tags normales de html, y un objeto con las distintas opciones, como template, que es el código html que más adelante se renderizará en el DOM.

<div id="foo">

<my-component></my-component>

</div>

<script>

/* primero registramos el componente */

Vue.component('my-component', {

template: '<h3>Soy un componente formidable</h3>'

});

/* Luego sacamos una instancia principal, donde se renderizará el componente que hay en el DOM */

var foo = new Vue({

el: '#foo'

});

</script>

De esta manera, el componente está disponible para cualquier instancia vue, es de naturaleza global; pero vue.js también permite definir componentes de manera local para que solo se puedan utilizar desde una instancia. Para eso, se define en la propia instancia en la propiedad components.

var foo = new Vue({

el: '#foo',

components: {

'local-component': {

template: '<p>soy un componente local ^^</p>'

}

}

});

Templates

En angular, la definición de las templates es muy sencilla: o bien se escribe directamente el código html en la propiedad template del controlador o bien se carga de forma dinámica mediante una llamada ajax al archivo indicado en templateUrl. En vue.js es algo más complejo, pues de momento no les convence la fórmula de templateUrl (aunque no descarto que en un futuro la implementen); pero a cambio tenemos más posibilidades para definir las templates. Las principales son:

1. Escribir directamente un string en el parámetro template, tal y como acabamos de ver, el cual, claro está, puede tener partes bindeadas con las dobles llaves (luego veremos por qué se construye de esa forma peculiar la propiedad data).

Vue.component('my-component', {

template: '<h3>{{title}}</h3>',

data: function() {

return {

title: 'Oh la la!'

}

}

});

2. Hacer lo mismo, pero con una template string, que al ser multilínea permite escribir el código con más comodidad y elegancia si el contenido es extenso.

template: `<div>

<h3>{{title}}</h3>

<p>Haga Basin</p>

</div>`,

Cuando se emplea una template string, los nodos deben estar contenidos en un solo nodo raíz.

3. Otra manera es leer el contenido html de algún sitio y luego volcarlo en la propiedad template. En estos casos, la fórmula más elegante es incluir ese contenido en un par de etiquetas script con un type que no sea javaScript, tal y como hace handelbars. Por convención, en vue este tipo se denomina text/x-template y se puede referenciar directamente en el componente sin necesidad de leer antes su contenido con innerHTML.

<script type="text/x-template" id="my-template">

<div>

<h3>{{title}}</h3>

<p>Haga Basin</p>

</div>

</script>

<script>

Vue.component('my-component', {

template: '#my-template',

...

4. Hay una cuarta manera para definir la template, pero la veremos en la siguiente entrada, cuando trate los componentes complejos.

Return data

Salvo algún que otro detalle, en los componentes podemos definir todas las propiedades que hemos visto hasta ahora: data, methods, computed properties. Uno de estos detalles es que data hay que incluirlo en una función y retornarlo para cambiar el contexto, pues de lo contrario los cambios de una propiedad afectarían a todas las instancias del componente; aunque por lo demás la lectura y el seteo de los valores de data no varían. Es decir, se antepone el this cuando se referencian dentro del mismo componente y en la template van con las dobles llaves (ver demo en fiddle).

....

Vue.component('my-component', {

template: '#my-template',

data: function() {

return {

foo: 'Humpty Dumpty'

}

},

methods: {

setFoo: function() {

this.foo = 'Tweedledee'

}

}

});

Árboles de componentes

Se podría hacer una aplicación web en la que cada pantalla fuera un solo componente, pero lo normal es que cada una esté formada por varios componentes ya que así dividimos el código en fragmentos más manejables y, además, podemos reutilizar aquellos que se repiten en las distintas partes, como sucede con la cabecera, el pie, los elementos de un formulario, etcétera. De hecho, esta es la gran ventaja de trabajar orientado a componentes, pues nos permite escribir menos código, el mantenimiento es más fácil y  tenemos que enviar menos información al cliente.

Para insertar un componente en otro es tan sencillo como incluirlo en la template respectiva y así ir articulándolos entre sí en una estructura arbórea hasta formar toda una pantalla.

...

<div id="app">

<parent-component></parent-component>

</div>

<script>

Vue.component('parent-component', {

template: ' <h3>Parent <child-component></child-component> </h3>'

});

Vue.component('child-component', {

template: '<h5>Child</h5>'

});

var myApp = new Vue({

el: '#app'

});

</script>

Como ocurre con otros frameworks orientados a componentes, el lío se produce cuando tratamos de comunicarlos entre sí. De arriba abajo, de un componente madre a su progenie, la información se pasa en atributos indicados en el html, los cuales se recogen en los descendientes en la propiedad props, que es un array.

Vue.component('parent-component', {

template: ' <h3>Parent <child-component my-prop="Humpty Dumpty"></child-component> </h3>'

});

Vue.component('child-component', {

props: ['myProp'],

template: '<h5>{{myProp}}</h5>'

});

...

Salvo que estemos usando template strings, los guiones de los atributos (kebab-case) se convierten en los componentes en mayúsculas (camel-case).

Propiedades dinámicas

Cuando el valor que se pasa del componente padre al hijo es dinámico, en lugar de una cadena estática como en el ejemplo anterior, el asunto se complica una miajica. Recordemos que la directiva v-bind permitía definir atributos de forma dinámica siguiendo esta sintaxis:

v-bind:atributo-a-definir="valor-del-atributo"

Por ejemplo, así asignaríamos una clase a este párrafo según valga fontasticClass en el data del componente:

<p v-bind:class="fontasticClass"></p>

Entonces, si queremos que la propiedad del hijo cambie dinámicamente según se modifica en el padre, lo que debemos hacer es v-bindearla.

Vue.component('parent-component', {

template: '#my-template',

data: function() {

return {

foo: 'Humpty Dumpty'

}

...

<child-component v-bind:my-prop="foo"></child-component>

De abajo arriba

A diferencia de angular, el seteo de las propiedades solo se produce desde los padres hacia los hijos. Es decir, si se cambia una propiedad en un componente hijo que se ha recibido de un padre, en este último no se reflejará el cambio. El equipo de vue argumenta que de esta manera evitan cambios accidentales, pero creo que es un error, pues para evitar esto hubiera bastado con añadir signos especiales en la definición de props, como en angular ('<', '@', ...), pero, en cualquier caso, nos proporcionan una herramienta bastante elegante para comunicarnos de abajo hacia arriba: los custom events.

Este tipo de eventos siguen una sintaxis similar a la que podemos encontrar en otras herramientas javaScript: se emite un evento (emit), que se está «escuchando» (on). En el caso de vue, el emit se lanza desde el componente hijo:

methods: {

setFooChild: function() {

this.$emit('fooChanged');

}

}

Y el on se indica entre los atributos del componente hijo en la template del padre y por valor lleva el método del componente padre que será invocado cuando se lance el emit.

<child-component v-bind:my-prop="foo" v-on:fooChanged="fooChangedParent"></child-component>

Como toda esta parte es algo enrevesada, he preparado un gist con un ejemplo.

Bueno, habría que comentar más cosas sobre los componentes básicos, pero de momento vamos a dejarlo aquí.

|| Tags: ,

Este artículo aún no ha sido valorado.

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Aportar un comentario

*