0% encontró este documento útil (0 votos)
268 vistas

Introducción: ¿Qué Es Vue - JS?

Vue.js es un framework progresivo para construir interfaces de usuario. Se enfoca en la capa de vista y es simple de usar e integrar con otros proyectos. Admite tanto aplicaciones pequeñas como grandes aplicaciones de una sola página. Los componentes permiten dividir una aplicación en partes más pequeñas y reutilizables. Las plantillas declarativas y las directivas como v-if y v-for vinculan dinámicamente los datos a la interfaz de usuario.

Cargado por

adri781
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
268 vistas

Introducción: ¿Qué Es Vue - JS?

Vue.js es un framework progresivo para construir interfaces de usuario. Se enfoca en la capa de vista y es simple de usar e integrar con otros proyectos. Admite tanto aplicaciones pequeñas como grandes aplicaciones de una sola página. Los componentes permiten dividir una aplicación en partes más pequeñas y reutilizables. Las plantillas declarativas y las directivas como v-if y v-for vinculan dinámicamente los datos a la interfaz de usuario.

Cargado por

adri781
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 77

Introducción

¿Qué es Vue.js?
Vue (pronunciado /vjuː/ en inglés, como view) es un framework progresivo para
construir interfaces de usuario. A diferencia de otros frameworks monolíticos, Vue está
diseñado desde el inicio para ser adoptado incrementalmente. La biblioteca principal
se enfoca solo en la capa de la vista, y es muy simple de utilizar e integrar con otros
proyectos o bibliotecas existentes. Por otro lado, Vue también es perfectamente capaz
de soportar aplicaciones sofisticadas de una sola página (en inglés single-page-
application o SPA) cuando se utiliza en combinación con herramientas
modernas y librerías compatibles.

Si eres un desarrollador de frontend con experiencia y quieres saber como Vue se


compara con otras bibliotecas/frameworks, revisa esta comparación.

Empezando

La guia oficial asume un conocimiento intermedio de HTML, CSS y JavaScript. Si eres


totalmente nuevo en el desarrollo de frontend, puede no ser la mejor idea empezar a
utilizar un framework - ¡aprende los conceptos básicos y luego regresa aquí! La
experiencia previa con otros frameworks ayuda, pero no es obligatoria.

La manera más sencilla de probar Vue.js es usando el ejemplo “hola mundo” en


JSFiddle. Siéntete libre de abrilo en otra pestaña y revisarlo a medida que
avanzamos con ejemlos básicos. Si no, puedes crear un archivo .html e incluir Vue con:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
La página de instalación provee más opciones para instalar Vue. Nota
que no recomendamos a los principiantes comenzar con vue-cli, especialmente si no
estás familiarizado con las herramientas de trabajo basadas en Node.js.
Renderizado declarativo

En el corazón de Vue.js se encuentra un sistema que nos permite renderizar


declarativamente datos en el DOM utilizando una sintaxis de plantillas directa:
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
Hello Vue!
¡Ya hemos creado nuestra primera aplicación Vue! Esto parece bastante similar a
renderizar una plantilla de texto, pero internamente Vue ha hecho muchas cosas. Los
datos y el DOM están enlazados, y todo es reactivo. ¿Cómo lo sabemos? Abre la
consola de JavaScript en tu navegador (ahora mismo, en esta página) y cambia el
valor de app.message. Deberías ver el ejemplo renderizado actualizarse acorde a lo que
has ingresado.

Además de interpolación de texto, tambíen podemos enlazar atributos de un


elemento, por ejemplo:
<div id="app-2">
<span v-bind:title="message">
¡Deja tu mouse sobre este mensaje unos segundos para ver el atributo `title` enlazado dinámicamente!
</span>
</div>
var app2 = new Vue({
el: '#app-2',
data: {
message: 'You loaded this page on ' + new Date()
}
})
¡Deja tu mouse sobre este mensaje unos segundos para ver el
atributo `title` enlazado dinámicamente!
Aquí nos encontramos con algo nuevo. El atributo v-bind que estás viendo es conocido
como una directiva. Las directivas llevan el prefijo v- para indicar que son atributos
especiales provistos por Vue y, como debes haber adivinado, aplican un
comportamiento reactivo especial al DOM renderizado. En este caso, básicamente
está diciendo “mantén el atributo title de este elemento enlazado con la
propiedad message en la instancia de Vue”.
Si abres nuevamente tu consola JavaScript y escribes app2.message = 'some new message',
verás que el HTML enlazado (en este caso, el atributo title) ha sido actualizado.

Condicionales y bucles

Es bastante sencillo alternar la presencia de un elemento:


<div id="app-3">
<p v-if="seen">Now you see me</p>
</div>
var app3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
Now you see me
Adelante, escribe app3.seen = false en la consola. Deberías ver desaparecer el mensaje.

Este ejemplo demuestra que no solo podemos enlazar datos con texto y atributos,
sino también con la estructura del DOM. Además, Vue provee un sistema de
transiciones muy poderoso que puede aplicar automáticamente efectos de
transición cuando los elementos son agregados/actualizados/removidos por Vue.
Hay unas cuantas otras directivas, cada una con una funcionalidad especial. Por
ejemplo, la directiva v-for puede ser utilizada para mostrar una lista de elementos
usando los datos de un array:
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
})

1. Learn JavaScript
2. Learn Vue
3. Build something awesome

En la consola, escribe app4.todos.push({ text: 'New item' }). Deberías ver un nuevo elemento
agregado a la lista.

Manejando entradas de usuario

Para permitir a los usuarios interactuar con tu aplicación, podemos usar la directiva v-
onpara añadir listeners de eventos que invocan métodos en nuestras instancias de
Vue:
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
Hello Vue.js!

Reverse Message

Nota que en el método simplemente actualizamos el estado de nuestra aplicación sin


modificar del DOM - todas las manipulaciones del mismo son manejadas por Vue, y el
código que escribes se enfoca en la lógica subyacente.
Vue también provee la directiva v-model que hace muy sencillo el enlace de dos vías
entre un input de un formulario y el estado de la aplicación:
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
Hello Vue!
Componentes

El sistema de componentes es otro concepto importante en Vue, porque es una


abstracción que nos permite construir aplicaciones de gran escala compuestas por
componentes pequeños, autocontenidos y, normalmente, reutilizables. Si lo
pensamos, casi cualquier tipo de interfaz gráfica de una aplicación puede ser
representada de manera abstracta como un árbol de componentes:

En Vue, un componente es esencialmente una instancia de Vue con opciones


predefinidas. Registrar un componente en Vue es directo y sencillo:
// Define un nuevo componente llamado todo-item
Vue.component('todo-item', {
template: '<li>This is a todo</li>'
})

Ahora puedes utilizarlo en la plantilla de otro componente:


<ol>
<!-- Crea una instancia del componente todo-item -->
<todo-item></todo-item>
</ol>

Pero esto renderizaría el mismo texto para cada todo, lo cual no es muy interesante.
Deberíamos ser capaces de pasar datos desde el padre a los componentes hijo.
Vamos a modificar la definición del componente para aceptar propiedades:
Vue.component('todo-item', {
// El componente todo-item ahora acepta
// "prop", el cual es similar a un atributo personalizado
// La rpopiedad se llama _todo_.
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
Ahora podemos pasar todo a cada componente repetido utilizando v-bind:
<div id="app-7">
<ol>
<!-- Ahora le pasamos a cada todo-item with el objeto todo -->
<!-- que representa, para que su contenido pueda ser dinámico -->
<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ text: 'Vegetables' },
{ text: 'Cheese' },
{ text: 'Whatever else humans are supposed to eat' }
]
}
})

1. Vegetables
2. Cheese
3. Whatever else humans are supposed to eat

Este es simplemente un ejemplo imaginario, pero hemos logrado separar nuestra


aplicación en porciones más pequeñas, y el hijo está razonablemente desacoplado del
padre a través de la interfaz de propiedades. Podemos mejorar aún más nuestro
componente <todo-item>con una plantilla más compleja o diferente lógica sin afectar a la
aplicación padre.

En aplicaciones grandes, es necesario dividir la aplicación entera en componentes


para un desarrollo manejable. Hablaremos mucho más acerca de los
componentes más adelante en la guia, pero aquí tienes un ejemplo (imaginario) de
como luciría una plantilla de aplicación utilizando componentes:
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>

Relación con los elementos personalizados

Puedes haber notado que los componentes de Vue son muy similares a
los Elementos Personalizados (Custom Elements), los cuales son parte de
la especificación de Componentes Web (Web Components). Esto es porque la
sintaxis de componente de Vue está modelada basándose en ideas de la
especificación. Por ejemplo, los componentes de Vue implementan la API de Slot y el
atributo especial is. Sin embargo, hay unas cuantas diferencias clave:

1. La especificación de Componentes Web todavía está en un estado de


borrador, y no está implementada nativamente en todos los navegadores. En
comparación, los componentes de Vue no requieren ningún polyfill y funcionan
consistentemente en todos los navegadores soportados (IE9 y superiores). Cuando se
necesite, los componentes de Vue pueden ser envueltos dentro de un elemento
personalizado nativo.

2. Los componentes de Vue proveen características importantes que no están


disponibles en los elementos personalizados, siendo las más notables el flujo de datos
entre componentes, la comunicación con eventos personalizados, y la integración con
herramientas de desarrollo.
La instancia de Vue

Constructor

Cada vm Vue es iniciada creando una instancia raíz de Vue con la función


constructora Vue:
var vm = new Vue({
// opciones
})
A pesar de no estar estrictamente asociado con el patrón MVVM, el diseño de Vue
estuvo parcialmente inspirado en él. Como convención, normalmente utilizamos la
variable vm(abreviación de ViewModel) para referirnos a instancias de Vue.

Cuando crees una nueva instancia de Vue, necesitas pasar un objeto de opciones el
cual puede contener opciones para datos, una plantilla, el elemento donde montarla,
métodos, callbacks para el ciclo de vida, etc. Puedes encontrar la lista completa de
opciones en la documentación de referencia de la API.
El constructor de Vue puede ser extendido para crear constructores de
componentesreutilizables con opciones predefinidas:
var MyComponent = Vue.extend({
// opciones de extensión
})
// todas las instancias de `MyComponent` son creadas con
// las opciones de extensión predefinidas
var myComponentInstance = new MyComponent()

Aunque es posible crear instancias extendidas imperativamente, la mayor parte del


tiempo es recomendable componerlas declarativamente in plantillas como elementos
personalizados. Hablaremos de ello en detalle en el sistema de componentes. Por
ahora, solo necesitas saber que todos los componentes de Vue son, esencialmente,
instancias de Vue extendidas.

Propiedades y métodos

Cada instancia de Vue proxies todas las propiedades que se encuentran en su


objeto data:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> verdadero
// modificar el valor de la propiedad también afecta a los datos originales
vm.a = 2
data.a // -> 2
// ... y viceversa
data.a = 3
vm.a // -> 3

Debe tenerse en cuenta


It should be noted that only these proxied properties are reactive. If you attach a new
property to the instance after it has been created, it will not trigger any view updates.
We will discuss the reactivity system in detail later.
In addition to data properties, Vue instances expose a number of useful instance
properties and methods. These properties and methods are prefixed with $ to
differentiate them from proxied data properties. For example:
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // -> true
vm.$el === document.getElementById('example') // -> true
// $watch is an instance method
vm.$watch('a', function (newVal, oldVal) {
// this callback will be called when `vm.a` changes
})
Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal
=> this.myMethod())). As arrow functions are bound to the parent context, this will not be the
Vue instance as you’d expect and this.myMethod will be undefined.

Consult the API reference for the full list of instance properties and methods.

Instance Lifecycle Hooks

Each Vue instance goes through a series of initialization steps when it is created - for
example, it needs to set up data observation, compile the template, mount the instance
to the DOM, and update the DOM when data changes. Along the way, it will also
invoke some lifecycle hooks, which give us the opportunity to execute custom logic.
For example, the created hook is called after the instance is created:
var vm = new Vue({
data: {
a: 1
},
created: function () {
// `this` points to the vm instance
console.log('a is: ' + this.a)
}
})
// -> "a is: 1"
There are also other hooks which will be called at different stages of the instance’s
lifecycle, for example mounted, updated, and destroyed. All lifecycle hooks are called with
their this context pointing to the Vue instance invoking it. You may have been
wondering where the concept of “controllers” lives in the Vue world and the answer is:
there are no controllers. Your custom logic for a component would be split among
these lifecycle hooks.
Lifecycle Diagram

Below is a diagram for the instance lifecycle. You don’t need to fully understand
everything going on right now, but this diagram will be helpful in the future.

Sintaxis de plantillas
Vue.js utiliza una sintaxis de plantilla basada en HTML lo que te permite enlazar
declarativamente el DOM con los datos de la instancia de Vue subyacente. Todas las
planitllas de Vue.js están compuestas por HTML válido que puede ser analizadas por
navegadores compatibles con las especifiaciones o analizadores HTML.

Internamente, Vue compila las plantillas a funciones de renderizado de DOM Virtual.


En combinación con el sistema de reactividad, Vue es capaz de descifrar
inteligentemente cual es la cantidad mínima de componentes a re-renderizar y aplicar
la menor cantidad posible de manipulaciones al DOM cuando el estado de la
aplicación cambia.

Si estas familiarizado con los conceptos del DOM Virtual y prefieres el poder de
JavaScript puro, puedes también escribir directamente funciones de
renderizado en lugar de plantillas, con soporte opcional para JSX.

Interpolations

Text

The most basic form of data binding is text interpolation using the “Mustache” syntax
(double curly braces):
<span>Message: {{ msg }}</span>
The mustache tag will be replaced with the value of the msg property on the
corresponding data object. It will also be updated whenever the data
object’s msg property changes.

You can also perform one-time interpolations that do not update on data change by
using the v-once directive, but keep in mind this will also affect any binding on the
same node:
<span v-once>This will never change: {{ msg }}</span>

HTML Puro

Las llaves dobles interpretan los datos como texto plano, no HTML. Si deseas mostrar
HTML real, necesitarás usar la directiva v-html:
<div v-html="rawHtml"></div>
El contenido es insertado como HTML puro - los enlaces de datos son ignorados.
Nota que no puedes utilizar v-html para componer plantillas parciales, porque Vue no
es un motor de plantillas basado en cadenas de texto. En su lugar, se prefiere utilizar
a los componentes como unidad fundamental para la reutilización de UI y la
composición.

Renderizar dinámicamente HTML arbitrario en tu sitio web puede ser muy peligroso ya
que conduce a vulnerabilidades XSS. Utiliza interpolación HTML solo con contenido
de confianza y nunca con contenido provisto por el usuario.

Atributos

Las llaves no deben ser utilizadas dentro de atributos HTML, en su lugar utiliza
la directiva v-bind:
<div v-bind:id="dynamicId"></div>
También funciona para atributos booleanos - el atributo sera quitado si la condición se
evalúa como falsa:
<button v-bind:disabled="someDynamicCondition">Button</button>

Utilizando expresiones JavaScript

Hasta ahora, solo hemos estado enlazando a propiedades simples en nuestras


plantillas. Pero Vue.js en realidad soporta todo el poder de las expresiones JavaScript
dentro de cualquier enlace con los datos:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

Estas expresiones serán evaluadas como JavaScript en el ámbito de datos de la


instancia de Vue. Una restricción es que cada enlace puede contener solo una
expresión simple, por lo que lo siguiente NO funcionará:
<!-- esto es una declaración, no una expresión: -->
{{ var a = 1 }}
<!-- el flujo de control tampoco funcionará, utiliza expresiones ternarias -->
{{ if (ok) return message }}
Las expresiones en las plantillas y tiene acceso restringido a una lista blanca de
variables globales como Math y Date. No debes intentar acceder a variables globales
definidas por el usuario dentro de expresiones en las plantillas.

Directivas

Las directivas son atributos especiales identificadas con el prefijo v-. Los valores de
los atributos de directivas deben ser una sola expresión JavaScript (con la
excepción de v-for, el cual discutiremos luego). El trabajo de una directiva es aplicar
reactivamente efectos secundarios al DOM cuando el valor de su expresión cambia.
Veamos el ejemplo que utilizamos en la introducción:
<p v-if="seen">Now you see me</p>
Aquí, la directiva v-if removería/insertaría el elemento <p> basada en la veracidad del
valor de la expresión seen.

Argumentos

Algunas directivas pueden recibir un “argumento”, indentificado con dos puntos luego
del nombre de la directiva. Por ejemplo, la directiva v-bind se utiliza para actualizar
reactivamente un atributo HTML:
<a v-bind:href="url"></a>
Aquí href es el argumento, el cual le indica a la directiva v-bind que enlace el
atributo href del elemento con el valor de la expresiónurl.
Otro ejemplo es la directiva v-on, la cual escucha eventos del DOM:
<a v-on:click="doSomething">

Aquí el argumento es el nombre del evento que debe escuchar. Hablaremos acerca
del manejo de enventos en detalle tambíen.

Modificadores

Los modificadores son sufijos especiales identificados con un punto, los cuales
indican que la directiva debe ser enlazada de alguna forma especial. Por ejemplo, el
modificador .preventindica a la directiva v-on que ejecute event.preventDefault() en el evento
disparado:
<form v-on:submit.prevent="onSubmit"></form>
Veremos más usos de los modificadores cuando hablemos en detalle de v-on y v-model.
Filtros

Vue.js te permite definir filtros que pueden ser usados para aplicar formatos de texto
comunes. Pueden ser utilizados en dos lugares: en la interpolación con llaves y las
expresiones v-bind. Los filtros deben ser agregados al final de las expresiones
JavaScript, luego de un símbolo de tubería:
<!-- en interpolación de texto -->
{{ message | capitalize }}
<!-- en v-bind -->
<div v-bind:id="rawId | formatId"></div>
Los filtros de Vue 2.x solo pueden ser usados dentro de interpolaciónes con llaves y
expresiones v-bind (esto último soportado desde la versión 2.1.0) porque están
diseñados principalmente para transformar texto. Para transformaciones de datos más
complejas en otras directivas, deberías utilizar en su lugar propiedades computadas.

Los filtros siempre reciben el valor de la expresión como primer parámetro.


new Vue({
// ...
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})

Pueden ser encadenados:


{{ message | filterA | filterB }}

Son funciones JavaScript, por lo que pueden recibir parámetros:


{{ message | filterA('arg1', arg2) }}
Aquí, la cadena de texto 'arg1' será pasada al filtro como segundo parámetro, y el valor
de la expresión arg2 será evaluado y pasado como tercer parámetro.
Atajos

El prefijo v- sirve como ayuda visual para identificar atributos específicos de Vue en


tus plantillas. Esto es útil cuando estás utilizando Vue.js para añadir comportamiento
dinámico a una estructura existente, pero puede tornarse repetitivo para algunas
directivas utilizadas frecuentemente. A la vez, la necesidad del prefijo v- se vuelve
menos importante cuando estás construeyendo una SPA donde Vue.js controla todas
las plantillas. Por lo tanto, Vue.js provee atajos especiales para dos de las directivas
más utilizadas, v-bind y v-on:

Atajo para v-bind
<!-- sintaxis completa -->
<a v-bind:href="url"></a>
<!-- atajo -->
<a :href="url"></a>

Atajo para v-on
<!-- sintaxis completa -->
<a v-on:click="doSomething"></a>
<!-- atajo -->
<a @click="doSomething"></a>
Pueden parecer un poco diferentes al HTML normal, pero : y @ son carácteres válidos
para nombres de atributo y todos los navegadores soportados por Vue.js los pueden
analizar correctamente. Además, no aparecen en la estructura renderizada final. La
sintaxis corta es totalmente opcional, pero seguramente te gustará cuando aprendas
más acerca de su uso.

Propiedades computadas y
observadores
Propiedades computadas

Las expresiones dentro de las plantillas son muy cómodas, pero están pensadas solo
para operaciones simples. Agregar demasiada lógica en tus plantillas puede hacerlas
engorrosas y difíciles de mantener. Por ejemplo:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
En este punto, la plantilla ya no es más sencilla y declarativa. Tienes que observarla
durante un momento antes de entender que muestra el valor de mesagge invertido. El
problema es peor cuando quieres incluir el mensaje invertido en más de un lugar
dentro de tu plantilla.

Por esto es que para cualquier lógica compleja, deberías utilizar una propiedad
computada.

Ejemplo básico

<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// un getter computado
reversedMessage: function () {
// `this` apunta a la instancia de vm
return this.message.split('').reverse().join('')
}
}
})

Resultado:
Original message: "Hello"

Computed reversed message: "olleH"


Aquí hemos declarado una propiedad computada reversedMessage. La función que
codificamos será usada como función getter para la propiedad vm.reversedMessage:
console.log(vm.reversedMessage) // -> 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // -> 'eybdooG'
Puedes abrir la consola y jugar con el ejemplo. El valor de vm.reversedMessage siempre
depende del valor de vm.message.
Puedes crear enlaces de datos a propiedades computadas en las plantillas como
harías con una propiedad normal. Vue está al tanto que vm.reversedMessage depende
de vm.message, por lo que actualizará cualquier enlace que dependa
de vm.reversedMessagecuando vm.message cambie. Y la mejor parte es que hemos creado
esta relación de dependencias declarativamente: la función getter computada no tiene
efectos secundarios, lo cual hace sencillo entenderla y probarla.

Cacheo computado vs Métodos

Puede que hayas notado que podemos lograr el mismo resultado invocando un
método en la expresión:
<p>Reversed message: "{{ reverseMessage() }}"</p>
// dentro del componente
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
En lugar de una propiedad computada, podemos definir la misma función como un
método. Desde el punto de vista del resultado, ambos enfoques son exactamente
iguales. Sin embargo, la diferencia es que las propiedades computadas son
cacheadas basándose en sus dependencias. Una propiedad computada solo se
reevaluará cuando alguna de sus dependencias haya cambiado. Esto significa que
mientras que message no haya cambiado, acceder reiteradamente a la propiedad
computada reversedMessage devolverá inmediatamente el resultado computado
previamente sin tener que ejecutar la función de nuevo.
Esto también significa que la siguiente propiedad computada nunca se actualizará,
porque Date.now() no es una dependencia reactiva:
computed: {
now: function () {
return Date.now()
}
}

En comparación, la invocación a un método siempre ejecutará la función cuando


haya un re-renderizado.

¿Para que necesitamos el cacheo? Imagina que tenemos una propiedad


computada A costosa en términos de rendimiento, la cual requiere iterar a través de
un arreglo enorme y hacer muchos cálculos. Además podemos tener otras
propiedades computadas que dependan de A. Sin cacheo, ¡estaríamos ejecutando la
función getter de A muchas más veces de las necesarias! En casos donde no quieras
cacheo, utiliza un método.

Propiedades computadas vs Propiedades observadas

Vue provee una forma más generica de observar cambios de datos en una instancia
de Vue y reaccionar frente a ellos: observar propiedades. Cuando tienes algunos
datos que deben cambiar basándose en otros datos, es tentador utilizar
excesivamente watch - especialmente si tienes experiencia con AngularJS. De
cualquier manera, normalmente es una mejor idea utilizar una propiedad computada
en lugar de una llamada imperativa al la función callbackwatch. Por ejemplo:
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})

El código anterior es imperativo y repetitivo. Compáralo con la versión con


propiedades computadas:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})

Mucho mejor, ¿no?


Función setter computada

Las propiedades computadas solo tienen una función getter por defecto, pero puedes
agregarles una función setter cuando lo necesites:
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
Ahora cuando ejecutes vm.fullName = 'John Doe', la función setter será ejecutada
y vm.firstName y vm.lastName serán actualizadas según corresponda.

Observadores

Mientras que las propiedades computadas son más apropiadas en la mayoría de los
casos, hay veces que un observador personalizado es necesario. Es por eso que Vue
provee una forma más genérica de reaccionar a los cambios en los datos a través de
la opción watch. Esto es mayormente útil cuando quieres realizar operaciones
asíncronas o costosas en respuesta a los cambios de los datos.
Por ejemplo:
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Dado que ya existe un ecosistema rico de bibliotecas AJAX -->
<!-- y colecciones de métodos utilitarios de propósito general, el núcleo de Vue -->
<!-- es capaz de mantenerse pequeño porque no crea los suyos propios. Esto también -->
<!-- te da la libertad de utilizar cualquier cosa con la que ya estes familiarizado -->
<script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
<script src="https://unpkg.com/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// cuando 'question' cambie, se ejecutará esta función
question: function (newQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce es una función probista por lodash para limitar cuan
// seguido una operación particularmente costosa puede ejecutarse.
// En este caso, queremos limitar las peticiones a
// yesno.wtf/api, esperando a que el usuario haya finalizado
// de tipear antes de hacer la petición ajax. Para aprender
// más acerca de la función _.debounce ( y su prima
// _.throttle), visita: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
},
// Este es el número de milisegundos que esperamos
// a que el usuario termine de tipear.
500
)
}
})
</script>

Resultado:

Ask a yes/no question: 

I cannot give you an answer until you ask a question!


En este caso, utilizar la opción watch nos permite realizar operaciones asíncronas
(acceder a una API), limitando cuan seguido ejecutamos esa operación, y establece
estados intermediarios hasta que tengamos una respuesta final. Nada de esto sería
posible sin una propiedad computada.
Además de la opción watch, puedes utilizar la API de vm.$watch.
Enlace de estilos y clases
Una necesidad común cuando se enlazan datos es manipular la lista de clases de un
elemento y sus estilos en línea. Dado que ambos son atributos, podemos utilizar v-
bind para manejarlos: solo necesitamos calcular la cadena de texto final con nuestras
expresiones. Sin embargo, lidiar con la concatenación de texto es molesto y propenso
a errores. Por este motivo, Vue provee mejoras especiales cuando v-bind se utiliza en
conjunto con class y style. Además de cadenas de texto, las expresiones peuden evaluar
también objetos o arreglos:

Enlazando clases HTML

Sintaxis de objeto

Podemos pasar un objeto a v-bind:class para intercambiar clases dinámicamente:


<div v-bind:class="{ active: isActive }"></div>
La sintaxis anterior indica que la presencia de la clase active será determinada por el
valor de verdad de la propiedad isActive.
Puedes intercambiar múltiples clases agregando más campos al objeto. Además, la
directiva v-bind:class puede co-existir con el atributo class. Entonces dada la siguiente
plantilla:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

Y los siguientes datos:


data: {
isActive: true,
hasError: false
}

Renderizará:
<div class="static active"></div>
Cuando isActive o hasError cambien, la lista de clases será actualizada en consecuencia.
Por ejemplo, si hasError pasa a valer true, la lista de clases se convertirá en "static active text-
danger".

El objeto enlazado no tiene que ser declarado en línea:


<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}

Esto renderizará el mismo resultado. Podemos también enlazar a una propiedad


computadaque devuelve un objeto. Este es un patrón muy común y poderoso:
<div v-bind:class="classObject"></div>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal',
}
}
}

Sintaxis de arreglo
Podemos pasar un arreglo a v-bind:class para aplicar una lista de clases:
<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}

Lo cual renderizará:
<div class="active text-danger"></div>

Si quisieras alternar una clase en la lista condicionalmente, puedes hacerlo con una
expresión ternaria:
<div v-bind:class="[isActive ? activeClass : '', errorClass]">
Esto siempre aplicará la clase errorClass, pero solo activeClass cuando isActivevalga true.

Sin embargo, esto puede resultar engorroso si tienes múltiples clases condicionales.
Es por eso que también es posible utilizar la sintaxis de objetos dentro de la sintaxis
de arreglos:
<div v-bind:class="[{ active: isActive }, errorClass]">

Dentro de componentes

Esta sección asume un conocimiento previo de los componentes de Vue.


Siéntete libre de saltearla y volver luego.

Cuando utilizas el atributo class en un componente personalizado, esas clases serán


agregadas al elemento raíz del componente. Las clases existentes en el elemento no
serán sobre escritas.

Por ejemplo, si declaras este componente:


Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})

Y luego agregas algunas clases cuando lo utilizas:


<my-component class="baz boo"></my-component>

El HTML renderizado será:


<p class="foo bar baz boo">Hi</p>

Lo mismo aplica a clases enlazadas:


<my-component v-bind:class="{ active: isActive }"></my-component>
Cuando isActive sea verdadero, el HTML renderizado será:
<p class="foo bar active">Hi</p>

Enlazando estilos en línea

Sintaxis de objeto

La sintaxis de objeto para v-bind:style es bastante directa - es similar al CSS puro,


excepto que es un objeto JavaScript. Puedes utilizar tanto camelCase como kebab-
case (utiliza comillas con kebab-case) para los nombres de las propiedades CSS:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}

Normalmente es una buena idea enlazar a un objeto de estilo directamente para que
la plantilla sea más clara:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}

Nuevamente, la sintaxis de objeto es utilizada normalmente en conjunto con


propiedades computadas que devuelven objetos.

Sintaxis de arreglo

La sintaxis de arreglo para v-bind:style te permite aplicar múltiples objetos de estilo al


mismo elemento:
<div v-bind:style="[baseStyles, overridingStyles]">

Prefijos automáticos

Cuando utilizas una propiedad CSS en v-bind:style que requiere prefijos, por


ejemplo transform, Vue lo detectará automáticamente y agregará los prefijos apropiados
a los estilos aplicados.

Renderizado condicional
v-if

En plantillas de cadena de texto, por ejemplo Handlebars, escribiríamos un bloque


condicional de la siguiente manera:
<!-- Plantilla de Handlebars -->
{{#if ok}}
<h1>Yes</h1>
{{/if}}
En Vue, utilizamos la directiva v-if para lograr el mismo resultado:
<h1 v-if="ok">Yes</h1>
También es posible agregar un bloque “else” con v-else:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
Grupos condicionales con v-if dentro de <template>
Debido a que v-if es una directiva, tiene que ser añadida a un elemento en particular.
Pero, ¿qué sucede si queremos mostrar/esconder más de un elemento? En este caso
utilizamos v-if en un elemento <template>, el cual sirve como elemento envolvente
invisible. El resultado del renderizado final no lo incluíra.
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

v-else
Puedes utilizar la directiva v-else para indicar un “bloque else” para v-if:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
Un elemento v-else debe encotrarse inmediatamente después de un elemento v-if o v-
else-if - de otra forma, no será reconocido.

v-else-if
Nuevo en 2.1.0

v-else-if, como su nombre lo indica, sirve como un “bloque else if” para v-if. Puede ser
encadenado varias veces seguidas:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
Similar a v-else, un elemento v-else-if debe ubicarse inmediatamente luego de un
elemento v-if o v-else-if.

Controlando elementos reusables con key

Vue intenta renderizar elementos de la manera más eficiente posible, normalmente


reutilizándolos en lugar de renderizarlos de cero. Más allá de ayudar a Vue a ser muy
rápido, esto puede tener algunas ventajas muy útiles. Por ejemplo, si un usuario
intenta alternar entre diferentes tipos de inicio de sesión:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
Intercambiando loginType en el código anterior no borarrá lo que el usuario ya haya
escrito. Dado que ambas plantillas utilizan los mismos elementos, <input> no es
reemplazado, solo su placeholder.

Verifícalo tu mismo escribiendo algo en el campo de texto y luego presionando el


botón “Toggle”:
Username 
Toggle login type
Sin embargo, este no siempre es el comportamiento deseado, por lo que Vue te
ofrece una manera de decir: “Estos dos elementos están completamente separados,
no los reutilices”. Simplemente agrega el atributo key con un valor único:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>

Ahora esos campos de texto serán renderizados desde cero cada vez que los
intercambies. Verifícalo:
Username 
Toggle login type
Nota que los elementos <label> están siendo reutilizados eficientemente, porque no
tienen el atributo key.

v-show
Otra opción para mostrar condicionalmente un elemento es la directiva v-show. El uso
es prácticamente el mismo:
<h1 v-show="ok">Hello!</h1>
La diferencia es que un elemento con v-show siempre será renderizado y permanecerá
en el DOM. v-show simplemente alterna el valor de la propiedad CSS display del
elemento.
Nota que v-show no soporta la sintaxis <template> ni funciona con v-else.

v-if vs v-show

v-if es renderizado condicional “real” porque se asegura que los listeners de eventos y


componentes hijo dentro del bloque condicional sean destruidos y recreados
apropiadamente durante los cambios de condición.
v-if es también lazy: si la condición es falsa durante el renderizado inicial, no hará
nada. El bloque condicional no será renderizado hasta que la condición sea verdadera
por primera vez.
En comparación. v-show es mucho más sencillo: el elemento siempre es renderizado
sin importar el estado inicial de la condición, con una alternancia basada en CSS.
Generalmente, v-if tiene un costo de alternancia mayor mientras que v-show tiene un
costo de renderizado inicial mayor. Entonces escoge v-show si necesitas alternar algo
muy frecuentemente o v-if si es poco probable que la condición cambie durante la
ejecución.

v-if with v-for

Cuando se utiliza en conjunto con v-for, v-for tiene una prioridad mayor que v-if. Lee


la guia de renderizado de listas para más detalles.

Renderizado de listas
v-for
Podemos utilizar la directiva v-for para renderizar una lista de elementos basándonos
en un arreglo. La directiva v-for requiere una sintaxis especial de la forma item in items,
donde items es el arreglo de datos fuente e item es un alias para el elemento sobre el
que se está iterando:

uso básico

<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})

Resultado:
 Foo
 Bar

Dentro de los bloques v-for tenemos acceso total a las propiedades del ámbito del
padre. v-for también soporta un segundo parámetro opcional para indicar el índice el
elemento actual.
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})

Resultado:

 Parent - 0 - Foo
 Parent - 1 - Bar

Puedes utilizar of como delimitador en lugar de in, para que la sintaxis sea más
parecida a la utilizada en JavaScript para iteradores:
<div v-for="item of items"></div>

v-for en
Similar a v-if, puedes utilizar una etiqueta <template> con v-for para renderizar un bloque
de múltiples elementos. Por ejemplo:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>

v-for con objetos
Puedes utilizar v-for para iterar a través de las propiedades de un objeto.
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#repeat-object',
data: {
object: {
firstName: 'John',
lastName: 'Doe',
age: 30
}
}
})

Resultado:

 John
 Doe
 30

También puedes proveer un segundo argumento para la llave:


<div v-for="(value, key) in object">
{{ key }} : {{ value }}
</div>

Y otro para el índice:


<div v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</div>
Cuando iteres sobre un objeto, el orden está basado en el orden de enumeración
de Object.keys(), el cual no garantizamos que sea consistente en todas las
implementaciones de motores JavaScript.

v-for con rangos
v-for puede recibir un entero. En este caso, repetirá la plantilla esa cantidad de veces.
<div>
<span v-for="n in 10">{{ n }}</span>
</div>

Resultado:
1 2 3 4 5 6 7 8 9 10

Componentes y v-for
Esta sección asume un conocimiento previo de los componentes de Vue.
Siéntete libre de saltearla y volver luego.

Puedes utilizar v-for directamente en un componente personalizado, como cualquier


otro elemento normal:
<my-component v-for="item in items"></my-component>

Sin embargo, no pasará automáticamente ningún dato al componente, porque los


componentes tienen ámbitos aislados propios. Para pasar los datos de la iteración al
componente, debes utilizar propiedades:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index">
</my-component>
La razón para no inyectar automáticamente item al componente es que genera un
acoplamiento estrecho entre el componente y el funcionamiento de v-for. Siendo
explícito acerca del origen de los datos hace que el componente sea reutilizable en
otras situaciones.

Aquí tienes un ejemplo completo de una lista de tareas pendientes:


<div id="todo-list-example">
<input
v-model="newTodoText"
v-on:keyup.enter="addNewTodo"
placeholder="Add a todo"
>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:title="todo"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">X</button>\
</li>\
',
props: ['title']
})
new Vue({
el: '#todo-list-example',
data: {
newTodoText: '',
todos: [
'Do the dishes',
'Take out the trash',
'Mow the lawn'
]
},
methods: {
addNewTodo: function () {
this.todos.push(this.newTodoText)
this.newTodoText = ''
}
}
})

 Do the dishes X
 Take out the trash X
 Mow the lawn X

v-for con v-if
Cuando existen en el mismo nodo, v-for tiene mayor prioridad que v-if. Esto significa
que v-if será ejecutado en cada iteración del bucle separadamente. Esto es muy útil
cuando quieres renderizar nodos solo para algunos elementos, como:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>

Lo anterior solo renderizará los todos que no hayan sido completados.


Si, en cambio, tu intención es saltearte condicionalmente la ejecución del bucle,
puedes utilizar v-if en un elemento envolvente (o <template>). Por ejemplo:
<ul v-if="shouldRenderTodos">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
key
Cuando Vue está actualizando una lista de elementos renderizados con v-for, por
defecto utiliza una estrategia “in-place patch”. Si el orden de los elementos en los
datos ha cambiado, en lugar de mover los elementos del DOM para coincidir con el
orden de los elementos en los datos, Vue simplemente actualizará cada elemento en
su lugar y se asegurará que refleje lo que debería ser renderizado en ese índice en
particular. Esto es similar al comportamiento de track-by="$index" en Vue 1.x.

Este modo por defecto es eficiente, pero solo conveniente cuando la lista


renderizada no depende del estado de componentes hijos o de estado
temporario del DOM (por ejemplo, campos de texto de un formulario).
Para darle una pista a Vue de que debe llevar un seguimiento de la identidad de cada
nodo, y entonces reusar y reordenar elementos existentes, necesitas proveer un
atributo key único para cada elemento. Un valor ideal para key sería un id único. Este
atributo especial es un equivalente aproximado a track-by en 1.x, pero funciona como un
atributo, por lo que necesitas utilizar v-bind para enlazarlo con valores dinámicos
(utilizamos la forma corta aquí):
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
Es recomendable proveer un key para v-for cuando sea posible , a menos que el
contenido del DOM iterado sea simple, o estarás confiando en el comportamiento por
defecto para las ganancias de rendimiento.
Dado que es un mecanismo genérico para identificar nodos, key también tiene otros
usos que no están relacionados específicamente con v-for, como veremos más
adelante en la guia.

Detección de cambios en un arreglo


Métodos de mutación

Vue envuelve los métodos de mutacion de los arreglos observados por lo que también
disparará actualizaciones de las vistas. Los métodos envueltos son:

 push()

 pop()

 shift()

 unshift()

 splice()

 sort()

 reverse()

Puedes abrir la consola y jugar con el arreglo items de los ejemplos anteriores


ejecutando sus métodos de mutación. Por ejemplo: example1.items.push({ message: 'Baz' }).

Reemplazando un arreglo

Los métodos de mutación, como su nombre sugiere, modifican el arreglo original


sobre el cual actúan. En comparación, hay también métodos no-mutantes, por
ejemplo: filter(), concat() y slice(), los cuales no modifican el arreglo original sino
que siempre devuelven un nuevo arreglo. Cuando trabajes con métodos no
mutantes, puedes simplemente reemplazar el arreglo anterior por el nuevo:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})

Puedes pensar que esto hará que Vue tiré todo el DOM existente y re-renderice la
lista entera. Por suerte, no es el caso. Vue implementa algunas heurísticas
inteligentes para reutilizar al máximo los elementos del DOM, por lo que reemplazar
un arreglo existente por otro que contiene objetos solapados es una operación muy
eficiente.
Advertencias

Debido a las limitaciones en JavaScript, Vue no puede detectar los siguientes


cambios en un arreglo:

1. Cuando intentas establecer el valor de un elemento a través del


índice: vm.items[indexOfItem] = newValue
2. Cuando intentas modificar el largo de un arreglo: vm.items.length = newLength

Para solucionar el problema 1, las siguientes dos opciones lograrán el mismo


resultado que vm.items[indexOfItem] = newValue, pero también dispararán actualizaciones de
estado en el sistema de reactividad:
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)
Para solucionar el problema 2, puedes utilizar también splice:
example1.items.splice(newLength)

Mostrando resultados filtrados/ordenados

En ocasiones queremos mostrar una versión filtrada u ordenada de un arreglo sin


tener que modificar o restablecer los datos originales. En este caso, puedes crear una
propiedad computada que devuelva el arreglo filtrado u ordenado:

Por ejemplo:
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
Como alternativa, puedes utilizar un método donde las propiedades computadas no
son factibles (por ejemplo, dentro de bucles v-for anidados):
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}

Enlaces con campos de formulario

Uso básico

Puedes utilizar la directiva v-model para crear enlaces de datos de dos vías en los


campos de un formulario. Esta directiva elige automáticamente la forma correcta de
actualizar el elemento basado en el tipo de campo. A pesar de parecer algo mágico, v-
model es esencialmente azúcar sintáctico para actualizar datos debido a eventos de
entrada de los usuarios, además de algunos cuidados para casos extremos.
v-model descarta los valores iniciales provistos por los campos de un formulario.
Siempre tratará a los datos en la instancia de Vue como fuente de verdad.
Para lenguajes que requieran un IME (Chinese, Japanese, Korean etc.), verás que v-
model no se actualiza durante la composición IME. Si deseas realizar estas
actualizaciones también, utiliza el evento instead en su lugar.

Texto

<input v-model="message" placeholder="edit me">


<p>Message is: {{ message }}</p>

Message is:

Texto multilínea

<span>Multiline message is:</span>


<p style="white-space: pre">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
Multiline message is:

La interpolación en textareas (<textarea>{{text}}</textarea>) no funcionará. Utiliza v-model en


su lugar.

Checkbox

Checkbox simple, valor booleano:


<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>

 false

Checkbox múltiples, enlazados al mismo arreglo:


<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
new Vue({
el: '...',
data: {
checkedNames: []
}
})

 Jack   John   Mike 


Checked names: []

Radio

<input type="radio" id="one" value="One" v-model="picked">


<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>

 One 
 Two 
Picked:
Select

Select simple:
<select v-model="selected">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>

    Selected:

Select múltiple (enlazado a un arreglo):


<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
   A  
Selected: []
Opciones dinámicas renderizadas con v-for:
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})

 Selected: A

Enlace de valores

Para radio, checkbox y opciones de select, los valores del enlace de v-model son


normalmente cadenas de texto estáticas (o booleanos en caso del checkbox):
<!-- `picked` es la cadena de texto "a" cuando se tilda -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` es 'true' o 'false' -->
<input type="checkbox" v-model="toggle">
<!-- `selected` es la cadena de texto "abc" cuando se selecciona -->
<select v-model="selected">
<option value="abc">ABC</option>
</select>
Pero a veces podemos querer enlazar el valor a una propiedad dinámica en la
instancia de Vue. Podemos utilizar v-bind para lograr esto. Además, utilizar v-bind nos
permite enlazar el valor del campo a valores de otros tipos:

Checkbox

<input
type="checkbox"
v-model="toggle"
v-bind:true-value="a"
v-bind:false-value="b"
>
// Cuando se tilda:
vm.toggle === vm.a
// cuando se destila:
vm.toggle === vm.b

Radio

<input type="radio" v-model="pick" v-bind:value="a">


// cuando se tilda:
vm.pick === vm.a

opciones de Select

<select v-model="selected">
<!-- objeto literal en línea -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// cuando se selecciona:
typeof vm.selected // -> 'object'
vm.selected.number // -> 123

Modificadores

.lazy
Por defecto, v-model sincroniza el campo con los datos luego de cada evento input (con
la excepción de las composiciones IME como se mencionó anteriormente). Puedes
agregar el modificador lazy para sincronizar luego de eventos change en su lugar:
<!-- sincronizado luego de "change" en lugar de "input" -->
<input v-model.lazy="msg" >

.number
Si deseas que un campo de usuario sea convertido automáticamente a un número,
puedes agregar el modificador number a tus campos controlados por v-model:
<input v-model.number="age" type="number">
Esto es útil, debido a que incluso con type="number", el valor de los elementos
HTML <ìnput> siempre devuelven una cadena de texto.

.trim
Si deseas que a los datos de usuario se le aplique trim automáticamente, puedes
agregar el modificador trim a tus campos controlados por v-model:
<input v-model.trim="msg">

v-model con componentes
Si todavía no estas familiarizado con los componentes de Vue, saltea esto por
ahora.

Los tipos de campos de formularios nativos de HTML no siempre se ajustarán a tus


necesidades. Afortunadamente, los componentes de Vue te permiten construir
campos de entrada reutilizables con comportamiento completamente personalizado.
¡Estos campos de entrada incluso funcionan con v-model! Para aprender más, lee
acerca de los campos de entrada personalizados en la guia de componentes.

Componentes

¿Qué son los componentes?

Los componentes son una de las características más poderosas de Vue. Te permiten
extender elementos HTML básicos para encapsular código reutilizable. En un nivel
alto, los componentes son elementos personalizados a los que el compilador de Vue
les añade comportamiento. En algunos casos, pueden aparecer como elementos
HTML nativos extendidos con el atributo especial is.

Utilizando componentes

Registro

Hemos aprendido en las secciones anteriores que podemos crear una nueva instancia
de Vue con:
new Vue({
el: '#some-element',
// opciones
})
Para registrar un componente global, puedes utilizar Vue.component(tagName, options). Por
ejemplo:
Vue.component('my-component', {
// opciones
})

Nota que Vue no te obliga a utilizar las reglas de W3C para nombres de etiquetas


personalizadas (todo en minúscula, con un guión medio) aunque seguir esta
convención es considerado una buena práctica.

Una vez registrado, un componente puede ser utilizado en la plantilla de una instancia
como un elemento personalizado <my-component></my-component>. Asegúrate que el
componente este registrado antes de crear la instancia principal de Vue. Aquí hay un
ejemplo completo:
<div id="example">
<my-component></my-component>
</div>
// registro
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// crear la instancia principal
new Vue({
el: '#example'
})

Lo cual renderizará:
<div id="example">
<div>A custom component!</div>
</div>
A custom component!

Registro local

No tienes que registrar cada componente globalmente. Puede hacer que un


componente este disponible solo en el ámbito de otro componente/instancia
registrándolo en la opción components:
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> solo estará disponible en la plantilla del padre
'my-component': Child
}
})

El mismo encapsulamiento aplica para otras características registrables de Vue, como


las directivas.
Advertencias en el análisis de plantillas del DOM

Cuando utilizas el DOM como tu plantilla (por ejemplo, utilizando la opción el para


montar un elemento con contenido existente), estarás sujeto a algunas restricciones
que son inherentes a como trabaja el HTML, porque Vue solo puede recuperar el
contenido de la plantilla luego que el navegador lo haya analizado y normalizado.
Incluso, algunos elementos como <ul>, <ol>, <table> y <select> tienen restricciones acerca
de que otros elementos pueden aparecer dentro de ellos, y otros como <option> solo
pueden aparecer dentro de ciertos otros.

Esto conducirá a problemas cuando se utilicen componentes personalizados con


elementos que tengas esas restricciones, por ejemplo:
<table>
<my-row>...</my-row>
</table>
El componente personalizado <my-row> será marcado como contenido inválido,
causando por ende errores en la salida renderizada. Una solución alternativa es
utilizar el atributo especial is:
<table>
<tr is="my-row"></tr>
</table>

Debe notarse que estas limitaciones no aplican si estás utilizando plantillas de


texto de alguna de las siguientes fuentes:

 <script type="text/x-template">

 Plantillas de texto JavaScript en línea


 Componentes .vue

Por lo tanto, prefiere utilizar plantillas de texto siempre que sea posible.

data debe ser una función


La mayoría de las opciones que pueden ser pasadas a un constructor de Vue pueden
ser utilizadas en un componente, con un caso especial: data debe ser una función. De
hecho, si intentas esto:
Vue.component('my-component', {
template: '<span>{{ message }}</span>',
data: {
message: 'hello'
}
})
Vue se detendrá y emitirá advertencias en la consola, diciéndote que data debe ser una
función para instancias de componentes. Es bueno entender por qué existe la regla,
así que hagamos algo de trampa:
<div id="example-2">
<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// 'data' es técnicamente una función, por lo que Vue no
// se quejará, pero estamos devolviendo la misma referencia
// de objeto en cada instancia de componente
data: function () {
return data
}
})
new Vue({
el: '#example-2'
})
0 0 0
Dado que las tres instancias del componente comparten el mismo objeto data,
¡incrementar un contador los incrementa a todos! Ouch. Arreglemos esto retornando
en su lugar un objeto de datos nuevo:
data: function () {
return {
counter: 0
}
}

Ahora todos nuestros contadores tienen su propio estado interno:


0 0 0

Componiendo componentes

Los componentes están pensados para ser utilizados en conjunto, comunmente en


relaciones padre-hijo: el componente A puede utilizar al componente B en su propiar
plantilla. Necesitarán inevitablemente comunicarse entre ellos: el padre puede
necesitar pasar datos hacia el hijo, mientras que el hijo puede necesitar informar de
algo que ha ocurrido al padre. Sin embargo, también es muy importante mantener lo
más posiblemente desacopados al padre e hijo a través de una interface definida
claramente. Esto asegura que el código de cada componente puede ser escrito y se
puede razonar aisladamente acerca de él, haciéndolos más mantenibles y
potencialmente fáciles de reutilizar.

In Vue.js, la relación padre-hijo entre componentes puede ser resumida


como propiedades hacia abajo, eventos hacia arriba. El padre pasa datos al hijo a
través de propiedades y el hijo envía mensajes al padre a través de eventos.
Veamos como trabajan.

Propiedades

Pasando datos a través de propiedades


Cada instancia de componente tiene su propio ámbito aislado. Esto significa que no
puedes (y no debes) referenciar directamente datos del padre en la plantilla del
componente hijo. Los datos pueden ser pasados hacia el hijo utilizando propiedades.
Una propiedad es un atributo personalizado para pasar información desde
componentes padres. Un componente hijo necesita declarar explícitamente las
propiedades que espera recibir utilizando la opción props:
Vue.component('child', {
// declara las propiedades
props: ['message'],
// como 'data', las propiedades pueden ser utilizadas dentro de las
// plantillas y está disponibles en la vm como this.message
template: '<span>{{ message }}</span>'
})

Entonces, puedes pasar una cadena de texto plana como:


<child message="hello!"></child>

Resultado:
hello!

camelCase vs. kebab-case

Los atributos HTML no distinguen entre mayúsculas y minúsculas, por lo que cuando
utilices plantillas que no sean de texto, los nombres de propiedades en
formato camelCase necesitan ser escritas con su equivalente kebab-case:
Vue.component('child', {
// camelCase en JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case en HTML -->
<child my-message="hello!"></child>
De nuevo, si estás utilizando plantillas de texto, entonces está limitación no aplica.

Propiedades dinámicas

Similar a enlazar atributos normales a una expresión, podemos utilizar v-bind para


enlazar dinámicamente propiedades con datos en el padre. Cuando los datos sean
actualizados en el padre, los cambios fluirán hacia el hijo:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
Normalmente es más sencillo utilizar la forma corta de v-bind:
<child :my-message="parentMsg"></child>

Resultado:
 
Message from parent

Literal vs dinámico

Un error común que los principiantes suelen cometer es tratar de pasar un número
utilizando la sintaxis literal:
<!-- esto pasa la cadena de texto "1" -->
<comp some-prop="1"></comp>
Sin embargo, dado que esta es una propiedad literal, su valor se pasa como la cadena
de texto "1" en lugar de un número. Si en realidad queremos pasar un número
JavaScript, necesitamos utilizar v-bind para que su valor sea evaluado como una
expresión JavaScript:
<!-- esto pasa el numero 1 -->
<comp v-bind:some-prop="1"></comp>
Flujo de datos en un solo sentido

Todas las propiedades establecen un enlace de un solo sentido entre la propiedad


del hijo y la del padre: cuando la propiedad del padre cambia, fuirá hacia el hijo, pero
no en el sentido inverso. Esto previene que los componentes hijo modifiquen
accidentalmente el estado del padre, lo cual puede hacer que el fujo de tu aplicación
sea díficil de razonar.

Además, cada vez que el componente padre es actualizado, todas las propiedades en
el componente hijo serán refrescadas con el último valor. Esto significa
que no deberías modificar una propiedad en un componente hijo. Si lo haces, Vue te
advertirá en la consola.

Hay usualmente dos casos en los que puede ser tentador modificar una propiedad:

1. La propiedad es utilizada solo para dar un valor inicial, el componente hijo solo
quiere utilizarla como una propiedad de datos local luego;

2. La propiedad es pasada como un valor crudo que luego necesita ser


transformado.

Las respuestas apropiadas a estos casos de uso son:

1. Define una propiedad de datos local que utilice el valor inicial de la propiedad
como su valor inicial:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}

2. Define una propiedad computada que sea calculada en base al valor de la


propiedad pasada:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}

Nota que los objetos y arreglos en JavaScript son pasados por referencia, por lo que
si la propiedad es un arreglo u objeto, modificarlos dentro del hijo afectará al estado
del padre.

Validación de propiedades

Es posible especificar en un compomnente requerimientos para las propiedades que


recibe. Si no se cumple con un requerimiento, Vue emitirá advertencias. Esto es
especialmente útil cuando estás creando componentes con la intención que sean
utlizados por otros.

En lugar de definir las propiedades como un arreglo de cadenas de texto, puedes


utilizar un objeto con los requerimientos de validación:
Vue.component('example', {
props: {
// verificación de tipo básica (`null` significa que acepta cualquier tipo)
propA: Number,
// múltiples tipos posibles
propB: [String, Number],
// cadena de texto requerida
propC: {
type: String,
required: true
},
// un número con valor por defecto
propD: {
type: Number,
default: 100
},
// Los valores por defecto de objectos/arreglos deben ser devueltos
// desde una función fábrica
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// función de validación personalizada
propF: {
validator: function (value) {
return value > 10
}
}
}
})
El type puede ser uno de los siguientes constructores nativos:

 String
 Number
 Boolean
 Function
 Object
 Array

Además, type puede ser una función constructora personalizada y la verificación será


hecha con instanceof.

Cuando una validación de propiedad falla, Vue producirá una advertencia en la


consola (si estás utilizando la versión de desarrollo).
Eventos personalizados

Hemos aprendido que los padres pueden pasar datos hacia los hijos utilizando
propiedades. Pero, ¿cómo nos comunicamos con el padre cuando algo sucede? Aquí
es donde el sistema de eventos personalizados de Vue entra en juego.

Utilizando v-on con eventos personalizados

Cada instancia de Vue implementa una interface de eventos, lo cual significa que


puede:

 Escuchar un evento utilizando $on(eventName)


 Emitir un evento utilizando $emit(eventName)

Nota que el sistema de eventos de Vue está separado de la API EventTarget API de
los navegadores. Aunque funcionan similarmente, $on y $emit not son alias
para addEventListener y dispatchEvent.
Además, un componente padre puede escuchar los eventos emitidos por un
componente hijo utilizando v-on directamente en la plantilla donde el componente hijo
está siendo utilizado.
No puedes utilizar $on para escuchar eventos emitidos por hijos. Debes utilizar v-
on directamente en la plantilla, como en el ejemplo debajo.

Aquí hay un ejemplo:


<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
0

0 0

En este ejemplo, es importante notar que el componente hijo todavía está


completamente desacoplado de lo que pasa fuera de él. Todo lo que hace es reportar
información acerca de su propia actividad, en caso de que a un componente padre
pueda importarle.
Enlazando eventos nativos a componentes
Puede haber momentos en los que quieras escuchar un evento nativo en el elemento
raíz de un componente. En estos casos, puedes utilizar el modificador .native para v-on.
Por ejemplo:
<my-component v-on:click.native="doTheThing"></my-component>

Componentes de campos de formularios utilizando eventos personalizados


Los eventos personalizados también pueden ser utilizados para crear campos
personalizados que funcionen con v-model. Recuerda:
<input v-model="something">

es simplemente azúcar sintáctico para:


<input v-bind:value="something" v-on:input="something = $event.target.value">

Cuando se utiliza con un componente, se simplifica a:


<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>
Entonces, para que un componente trabaje con v-model, debe:

 aceptar una propiedad value


 emitir un evento input con el nuevo valor

Veámoslo en acción con un ejemplo muy sencillo de un campo de entrada de dinero:


<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
',
props: ['value'],
methods: {
// En lugar de actualizar el valor directamente, este
// método es utilizado para dar formato y aplicar restricciones
// al valor de la entrada
updateValue: function (value) {
var formattedValue = value
// Remueve espacios en blanco de ambos lados
.trim()
// Acorta a dos decimales
.slice(0, value.indexOf('.') + 3)
// Si el valor no estaba normalizado aún,
// lo sobrescribimos manualmente
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// Emite el valor numérico a través del evento 'input'
this.$emit('input', Number(formattedValue))
}
}
})

La implementación de arriba es bastante inocente. Por ejemplo, los usuarios pueden


ingresar múltiples puntos e incluso letras algunas veces, ¡Aagh! Para aquellos que
quieran ver un ejemplo no tan trivial, aquí hay un ejemplo más robusto de un campo
de entrada de dinero:

La interface de eventos puede ser usada para crear campos de entrada poco
comunes. Por ejemplo, imagina estas posibilidades:
<voice-recognizer v-model="question"></voice-recognizer>
<webcam-gesture-reader v-model="gesture"></webcam-gesture-reader>
<webcam-retinal-scanner v-model="retinalImage"></webcam-retinal-scanner>

Comunicación entre componentes sin relación padre/hijo

En ocasiones dos componentes pueden necesitar comunicarse entre ellos pero no


son padre/hijo. En escenarios simplres, puedes utilizar una instancia de Vue vacía
como un bus central de eventos:
var bus = new Vue()
// en un método del componente A
bus.$emit('id-selected', 1)
// en el _hook created_ del componente B
bus.$on('id-selected', function (id) {
// ...
})

En escenarios más complejos, deberías considerar emplear un patrón de manejo de


estado dedicado.

Distribución de contenido con slots

Cuando se utilizan componentes, es usual querer componerlos como:


<app>
<app-header></app-header>
<app-footer></app-footer>
</app>

Hay dos cosas a notar aquí:


1. El componente <app> no sabe que contenido puede ser necesario en el
elemento donde será montado. Eso lo decide cualquier componente que esté
utilizando <app>.
2. El componente <app> probablemente tenga su propia plantilla.
Para lograr que la composición funcione, necesitamos una manera de entrelazar el
“contenido” del padre y la propia plantilla del componente. Este es un proceso
llamado distribución de contenido (o “transclusión” si estás familiarizado con
Angular). Vue.js implementa una API de distribución de contenido que está modelada
basada en el borrador actual de la especificación de componentes web, utilizando
el elemento especial <slot>para trabajar como contenedor de distribución para el
contenido original.

Ámbito de compilación
Antes de sumergirnos en la API, clarifiquemos primero en que ámbito son compilados
los contenidos. Imagina una plantilla como esta:
<child-component>
{{ message }}
</child-component>
¿message debería estar enlazado a los datos del padre o del hijo? La respuesta es al
padre. Suna The answer is the parent. Una regla sencilla para recordar el ámbito de
los componentes es:

Todo lo que se encuentre en la plantilla del padre es compilado en el ámbito del


padre; todo lo que se encuentra en la plantilla del hijo, es compilado en el
ámbito del hijo.

Un error común es tratar de enlazar una directiva en la plantilla del padre a un


método/propiedad del hijo:
<!-- NO funciona -->
<child-component v-show="someChildProperty"></child-component>
Asumiendo que someChildProperty es una propiedad en el componenten hijo, los ejemplos
anteriores no funcionarán. La plantilla del padre no tiene conocimiento del estado de
un componente hijo.

Si necesitas enlazar directivas en el ámbito de un hijo en el nodo raíz de un


componente, debes hacerlo en la plantilla propia de ese componente:
Vue.component('child-component', {
// esto funciona, porque estamos en el ámbito correcto
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})

De manera similar, el contenido distribuido será compilado en el ámbito padre.


Slot único

El contenido del padre será descartado a menos que la plantilla del componente hijo
contenga por lo menos un contenedor <slot>. Cuando haya solo un slot sin atributos, el
contenido completo será insertado en su posición en el DOM, reemplazandolo.
Cualquier contenido originalmente dentro de la etiqueta <slot> es considerado por
defecto. El contenido por defecto es compilado en el ámbito del hijo y solo será
mostrado si el elemento de alojamiento está vacío y no tiene contenido para ser
insertado.
Imagina que tenemos un componente llamado my-component con la siguiente plantilla:
<div>
<h2>I'm the child title</h2>
<slot>
This will only be displayed if there is no content
to be distributed.
</slot>
</div>

Y el padre que utiliza el componente:


<div>
<h1>I'm the parent title</h1>
<my-component>
<p>This is some original content</p>
<p>This is some more original content</p>
</my-component>
</div>

El resultado renderizado será:


<div>
<h1>I'm the parent title</h1>
<div>
<h2>I'm the child title</h2>
<p>This is some original content</p>
<p>This is some more original content</p>
</div>
</div>
Slots con nombre

Los elementos <slot> tienen un atributo especial, name, el cual puede ser utilizado para


personalizar aún más como debe ser distribuido el contenido. Puedes tener
múltiples slots con diferentes nombres. Un slot con nombre se emparejará con
cualquier elemento que tenga su correspondiente atributo slot en el fragmento de
contenido.

Todavía puede haber un slot sin nombre, el cual es el slot por defecto que captura


cualquier contenido que no haya coincidido anteriormente. Si no hay un slot por
defecto, el contenido que no haya coincidido será descartado.
Por ejemplo, imagina que tenemos un componente app-layout con la siguiente plantilla:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>

La estructura del padre:


<app-layout>
<h1 slot="header">Here might be a page title</h1>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</app-layout>

El renderizado resultante será:


<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>

La API de distribución de contenido es un mecanismo muy útil cuando se diseñan


componentes pensados para utilizarse compuestos con otros.

Slots con ámbito

Nuevo en 2.1.0

Un slot con ámbito es un tipo especial de slot que funciona como una plantilla


reusable (a la que se pueden pasar datos) en lugar de elementos-ya-renderizados.

En un componente hijo, simplemente pasa datos a un slot como si estuvieses


pasando propiedades a un componente:
<div class="child">
<slot text="hello from child"></slot>
</div>
En el padre, un elemento <template> con el atributo especial scope indica que es una
plantilla para un slot con ámbito. El valor de scope es el nombre de una variable
temporaria que mantiene el objeto de propiedades pasado desde el hijo:
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>

Si renderizamos lo anterior, la salida será:


<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>

Un caso de uso típico para slots con ámbito sería un componente lista que permita al
consumidor del componente personalizar como debería ser renderizado cada
elemento en la lista:
<my-awesome-list :items="items">
<!-- los 'slots' con ámbito pueden también tener nombre -->
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list>

Y la plantilla para el componente lista:


<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- contenido por defecto aquí -->
</slot>
</ul>

Componentes dinámicos
Puedes utilizar el mismo punto de montaje e intercambiar dinámicamente múltiples
componentes utilizando el elemento reservado <component> y enlazarlo dinámicamente a
su atributo is:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- ¡el componente cambia cuando vm.currentView cambia! -->
</component>

Si prefieres, puedes enlazar directamente a objetos de componente:


var Home = {
template: '<p>Welcome home!</p>'
}
var vm = new Vue({
el: '#example',
data: {
currentView: Home
}
})

keep-alive
Si quieres mantener en memoria los componentes que han sido sacados para
preservar su estado o evitar un re-renderizado, puedes envolver un componente
dinámico con un elemento <keep-alive>:
<keep-alive>
<component :is="currentView">
<!-- ¡los componentes inactivos serán guardados en una memoria caché! -->
</component>
</keep-alive>
Más detalles acerca de <keep-alive> en la referencia de la API.

Misc

Creando componentes reusables

Cuando crees componentes, es bueno tener en cuenta si tu intención es reutilizarlo en


algún otro lugar luego. Está bien que componentes de un solo uso estén
estrechamente acoplados, pero los componentes reusables deben definir una
interface pública limpia y no suponer nada acerca del contexto en el que está siendo
utilizado.

La API para un componente de Vue se divide en tres partes: propiedades, eventos


y slots:

 Las propiedades permiten al ambiente externo pasar datos al componente

 Los eventos permiten al componente disparar efectos secundarios en el


ambiente externo

 Los Slots permiten al ambiente externo componer al componente con


contenido extra
Con la sintaxis corta para v-bind y v-on, las intenciones pueden ser comunicadas clara y
exitosamente a la plantilla:
<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat"
>
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>

Referencia a componentes hijo

A pesar de la existencia de propiedades y eventos, a veces puede que necesites


acceder directamente a un componente hijo en JavaScript. Para lograr esto tienes que
asignarle un ID de referencia utilizando ref. Por ejemplo:
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// accede a la instancia del componente hijo
var child = parent.$refs.profile
Cuando ref se utiliza en conjunto con v-for, la referencia que obtendrás será un arreglo
de objetos que contienen componentes hijos espejados con la funte de datos.
$refs son asignadas luego de que el componente haya sido renderizado, y no es
reactivo. Está pensado como una vía de escape para la manipulación directa de hijos.
Debes evitar utilizar $refs en las plantillas o propiedades computadas.

Componentes asíncronos

En aplicaciones grandes, puede que necesitemos dividir la aplicación en porciones


mas chicas y cargar un componente desde el servidor solo cuando es en realidad
utilizado. Para hacer esto más sencillo, Vue te permite definir tus componentes como
una función fabrica que resuelve asíncronicamente la definición de ese componente.
Vue ejecutará la función fábrica solo cuando el componente necesite ser renderizado
y guardará en memoria caché el resultado para futuras re-renderizaciones. Por
ejemplo:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pasa la definición del componente a la función callback de resolución
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
La función fábrica recibe una función callback resolve, la cual debe ser llamada cuando
hayas recuperado la definición del componente desde tu servidor. Puedes también
ejecutar reject(reason) para indicar que la carga ha fallado. setTimeout aquí está
simplemente como demostración. Como se recupera el componente es una decisión
totalmente a tu criterio. Un enfoque recomendado es utilizar los componentes
asíncronos junto con la característica de división de código de Webpack:
Vue.component('async-webpack-example', function (resolve) {
// Esta sintaxis 'require' especial indicará a Webpack que
// automáticamente divida el código de tu módulos en diferentes módulos
// los cuales serán cargados a través de peticiones Ajax.
require(['./my-async-component'], resolve)
})
Puedes devolver también un Promise en la función fábrica, por lo que con la sintaxis de
Webpack 2 + ES2015 puedes hacer:
Vue.component(
'async-webpack-example',
() => import('./my-async-component')
)

Si eres un usuario Browserify y te gustaría utilizar componentes asíncronos,


desafortunadamente su creador ha dejado muy claro que la carga asíncrona “no es
algo que Browserify soportará nunca”. Oficialmente al menos. La comunidad ha
encontrado algunas soluciones alternativas, las cuales pueden ser útiles para
aplicaciones complejas ya existentes. Para cualquier otro escenario, recomendamos
utilizar Webpack para tener soporte asíncrono de primera clase incorporado.

Convención de nombres de componentes

Cuando registres componentes (o propiedades), puedes utilizar kebab-case,


camelCase, o TitleCase. A Vue no le interesa.
// en una definición de componente
components: {
// registra utilizando kebab-case
'kebab-cased-component': { /* ... */ },
// registra utilizando camelCase
'camelCasedComponent': { /* ... */ },
// registra utilizando TitleCase
'TitleCasedComponent': { /* ... */ }
}

Sin embargo, dentro de las plantillas HTML, debes utilizar kebab-case:


<!-- siempre utiliza kebab-case en plantillas HTML -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<title-cased-component></title-cased-component>

Sin embargo, cuando utilices plantillas de texto, no estás limitado por las restricciones
de HTML. Eso significa que incluso en las plantillas, puedes referenciar a tus
componentes y propiedades utilizando camelCase, TitleCase, o kebab-case:
<!-- ¡utiliza lo que quieras en plantillas de texto! -->
<my-component></my-component>
<myComponent></myComponent>
<MyComponent></MyComponent>
Si tu componente no recibe contenido a través de elementos slot, puedes incluso hacer
que se auto-cierre con una / luego del nombre:
<my-component/>

De nuevo, esto solo funciona con plantillas de texto, dado que los elementos


personalizados auto-cerrados no son HTML válido y el analizador nativo de tu
navegador no los entenderá.

Componentes recursivos

Los componentes pueden invocarse recursivamente en su propia plantilla. Sin


embargo, solo pueden hacerlo con la opción name:
name: 'unique-name-of-my-component'
Cuando registres un componente globalmente utilizando Vue.component, el ID global es
asignado automáticamente como la opción name del componente.
Vue.component('unique-name-of-my-component', {
// ...
})

Si no eres cuidadoso, los componentes recursivos pueden conducir a bucles infinitos:


name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'
Un componente como el anterior resultará en un error “tamaño máximo de pila
excedido”, así que asegúrate que la invocación recursiva sea condicional (quiere
decir, utiliza un v-ìf que eventualmente será false).

Referencias circulares entre componentes

Digamos que estas construyendo un árbol de directorios, como en Finder o


Explorador de archivos. Puede que tengas un componente tree-folder con esta plantilla:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
Luego un componente tree-folder-contents con esta otra plantilla:
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
Cuando miras detenidamente, verás que estos componentes serán en realidad
descendientes yancestros uno del otro en el árbol, ¡una paradoja! Cuando registras
componentes globalmente con Vue.component, esta paradoja se resuelve
automáticamente por ti. Si es tu caso, deja de leer aquí.

Sin embargo, si estas requiriendo/importando componentes utilizando un sistema de


módulos, por ejemplo Webpack o Browserify, obtendrás un error:
Failed to mount component: template or render function not defined.

Para explicar lo que está sucediendo, llamaré a nuestros componentes A y B. El


sistema de módulos ve que necesita a A, pero primero A necesita a B, pero B necesita
a A, pero A necesita a B, etc. Queda trabado en un bucle, no sabiendo como resolver
adecuadamente cada componente sin resolver primero el otro. Para arreglar esto,
necesitamos indicarle al sistema de módulos un punto en el cual pueda decir: “A
necesitará a B en algún momento, pero no es necesario resolver B primero”.
En nuestro caso, haré que ese punto sea el componente tree-folder. Sabemos que el hijo
que crea la paradoja es el componente tree-folder-contents, por lo que esperaremos
al hook del ciclo de vida beforeCreate para registrarlo:
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
}

¡Problema resuelto!

Plantillas en línea
Cuando el atributo especial inline-template está presente en un componente hijo, el
componente utilizará su propio contenido interno como su plantilla, en lugar de tratarlo
como contenido distribuido. Esto permite una mayor flexibilidad en la creación de
plantillas.
<my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</my-component>
Sin embargo, inline-template hace más difícil razonar acerca del ámbito de nuestra
plantilla. Como una buena práctica, es preferible definir plantillas dentro de
componentes utilizando la opción template o en un elemento template dentro de un
archivo .vue.

X-Templates

Otra forma de definir plantillas es dentro de un elemento script con el tipo text/x-template, y


luego referenciando la plantilla con un id. Por ejemplo:
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})

Esto puede ser útil para demostraciones con plantillas grandes o en aplicaciones
extremadamente pequeñas, pero debe ser evitado en otro caso, porque separan las
plantillas del resto de la definición del componente.

Componentes estáticos baratos con v-once


Renderizar HTML plano es muy rápido en Vue, pero en ocasiones puede que tengas
un componente que contiene mucho contenido estático. En estos casos, puedes
asegurarte que sea evaluado solo una vez y agregado a la memoria caché
añadiéndole la directiva v-once al elemento raíz, de la siguiente forma:
Vue.component('terms-of-service', {
template: '\
<div v-once>\
<h1>Terms of Service</h1>\
... a lot of static content ...\
</div>\
'
})

También podría gustarte