- Aprender
-
Ecosistema
Ayuda
Herramientas
Librerías Oficiales
Noticias
Lista de Recursos
- Equipo
- Apoyar Vue
- Traducciones
Guía
Conocimientos Esenciales
- Instalación
- Introducción
- La instancia Vue
- Sintaxis de Template
- Propiedades Computadas y Observadores
- Enlace Clases y Estilos
- Renderización Condicional
- Renderizado de lista
- Manejo de eventos
- Binding en Formularios
- Conceptos Básicos de Componentes
Transiciones & Animaciones
- Efectos de Transición
Componentes en Profundidad
- Registro de Componente
- Propiedades
- Eventos personalizados
- Slots
- Componentes Dinámicos & Asíncronos
- Handling Edge Cases
- Transiciones de estado
Reusabilidad & Composición
- Mixins
- Directivas Personalizadas
- Funciones de renderizado & JSX
- Plugins
- Filtros
- Publicación en Producción
Herramientas
- Componentes de un Solo Archivo (Single File Components)
- Testing Unitario
- Soporte TypeScript
Escalando la Aplicación
- Enrutamiento
- Administración del Estado
- Renderizado del lado de Servidor
Funcionamiento Interno
- Reactividad en profundidad
Migraciones
- Migración desde Vue 1.x
- Migración desde Vue Router 0.7.x
- Migración de Vuex 0.6.x a 1.0
Meta Documentación
- Comparación con otros frameworks
- Únete a la comunidad Vue.js!
- Conozca al equipo
Funciones de renderizado & JSX
Lo esencial
Vue recomienda utilizar plantillas para construir su HTML en la gran mayoría de los casos. Sin embargo, hay situaciones en las que usted realmente necesita el poder programático completo de JavaScript. Ahí es donde puede usar la función render, una alternativa más cercana al compilador para las plantillas.
Vayamos a un ejemplo simple donde una función render
sería práctica. Digamos que usted quiere generar encabezados anclados:
<h1> |
Para el código HTML anterior, usted decide que desea esta interfaz de componente:
<anchored-heading :level="1">Hola mundo!</anchored-heading> |
Cuando usted comienza con un componente que solo genera un encabezado basado en la propiedad level
, rápidamente llega a esto:
<script type="text/x-template" id="anchored-heading-template"> |
Vue.component('anchored-heading', { |
Esa plantilla no se siente muy bien. No solo es muy detallada, sino que estamos duplicando <slot></slot>
para cada nivel de encabezado y tendremos que hacer lo mismo cuando agreguemos el elemento delimitador.
Si bien las plantillas funcionan bien para la mayoría de los componentes, está claro que este no es uno de ellos. Así que intentemos reescribirlo con una función render
:
Vue.component('anchored-heading', { |
¡Mucho más simple! El código es más corto, pero también requiere una mayor familiaridad con las propiedades de la instancia de Vue. En este caso, debe saber que cuando pasa hijos sin un atributo slot
a un componente, como el ¡Hola mundo!
dentro de anchored-heading
, esos hijos se almacenan en la instancia del componente en $slots.default
. Si usted no lo ha hecho ya, es recomendable que lea API de propiedades de instancia antes de ahondar en las funciones render.
Nodos, árboles y el DOM virtual
Antes de sumergirnos en las funciones de render, es importante saber un poco sobre cómo funcionan los navegadores. Considere el siguiente HTML como ejemplo:
<div> |
Cuando un navegador lee este código, construye un árbol de “nodos DOM” para ayudarlo a realizar un seguimiento de todo, tal como podría crear un árbol genealógico para realizar un seguimiento de su familia completa.
El árbol de nodos del DOM para el HTML anterior se ve así:
Cada elemento es un nodo. Cada pieza de texto es un nodo. Incluso los comentarios son nodos! Un nodo es solo una parte de la página. Y como en un árbol genealógico, cada nodo puede tener hijos (es decir, cada pieza puede contener otras piezas).
Actualizar todos estos nodos de manera eficiente puede ser difícil, pero afortunadamente, nunca tiene que hacerlo manualmente. En cambio, usted le dice a Vue qué HTML quiere en la página, en una plantilla:
<h1>{{ blogTitle }}</h1> |
O en una función render:
render: function (createElement) { |
En ambos casos, Vue mantiene la página actualizada automáticamente, incluso cuando blogTitle
cambie.
El DOM Virtual
Vue logra esto al crear un DOM virtual para realizar un seguimiento de los cambios que debe realizar en el DOM real. Mirando de cerca esta línea:
return createElement('h1', this.blogTitle) |
¿Qué está devolviendo createElement
? No es exactamente un elemento DOM real. Tal vez podría llamarse con más precisión createNodeDescription
, ya que contiene información que describe a Vue qué tipo de nodo debe representar en la página, incluidas las descripciones de los nodos hijos. Llamamos a esta descripción de nodo “nodo virtual”, generalmente abreviado a VNode. “DOM virtual” es lo que llamamos el árbol completo de VNodes
, construido por un árbol de componentes de Vue.
Argumentos createElement
Lo siguiente con lo que tendrá que familiarizarse es cómo usar las características de la plantilla en la función createElement
. Aquí están los argumentos que createElement
acepta:
// @returns {VNode} |
El objeto de datos en profundidad
Una cosa a tener en cuenta: similar a como v-bind:class
yv-bind:style
tienen un tratamiento especial en las plantillas, tienen sus propios campos de nivel superior en los objetos de datos VNode. Este objeto también le permite enlazar atributos HTML normales así como propiedades del DOM como innerHTML
(esto reemplazaría la directiva v-html
):
{ |
Ejemplo Completo
Con este conocimiento, ahora podemos terminar el componente que comenzamos:
var getChildrenTextContent = function (children) { |
Restricciones
Los VNodes deben ser únicos
Todos los VNodes en el árbol de componentes deben ser únicos. Eso significa que la siguiente función de render no es válida:
render: function (createElement) { |
Si realmente quiere duplicar el mismo elemento/componente muchas veces, puede hacerlo con una función de fábrica. Por ejemplo, la siguiente función de representación es una forma perfectamente válida de representar 20 párrafos idénticos:
render: function (createElement) { |
Reemplazando las funcionalidades de la plantilla con JavaScript plano
v-if
y v-for
Donde sea que se pueda lograr algo fácilmente en JavaScript simple, las funciones de renderizado de Vue no proporcionan una alternativa propietaria. Por ejemplo, en una plantilla usando v-if
y v-for
:
<ul v-if="items.length"> |
Esto podría ser reescrito con los if
/else
y map
de JavaScript en una función de renderizado:
props: ['items'], |
v-model
No hay una contraparte directa de v-model
en las funciones de renderizado, tendrá que implementar la lógica usted mismo:
props: ['value'], |
Este es el costo de ir a un nivel inferior, pero también le da mucho más control sobre los detalles de interacción en comparación con el v-model
.
Modificadores de eventos y claves
Para los modificadores de eventos .passive
,.capture
y .once
, Vue ofrece prefijos que se pueden usar conon
:
Modificador(es) | Prefijo |
---|---|
.passive |
& |
.capture |
! |
.once |
~ |
.capture.once or.once.capture |
~! |
Por ejemplo:
on: { |
Para todos los demás modificadores de evento y clave, no es necesario ningún prefijo propietario, porque puede usar métodos de evento en el controlador:
Modificador(es) | Equivalente en el Manejador |
---|---|
.stop |
event.stopPropagation() |
.prevent |
event.preventDefault() |
.self |
if (event.target !== event.currentTarget) return |
claves:.enter , .13 |
if (event.keyCode !== 13) return (cambia 13 a otro código clave para otros modificadores clave) |
Teclas modificadoras:.ctrl , .alt , .shift , .meta |
if (!event.ctrlKey) return (cambia ctrlKey a altKey , shiftKey , o metaKey , respectivamente) |
Aquí hay un ejemplo con todos estos modificadores usados juntos:
on: { |
Slots
Puede acceder a los contenidos de los slots estáticos como Arrays de VNodes desde this.$slots
:
render: function (createElement) { |
Y acceder a los slots con alcance como funciones que devuelven VNodes desde this.$scopedSlots
:
props: ['message'], |
Para pasar slots con alcance a un componente secundario utilizando funciones de representación, use el campo scopedSlots
en los datos de un VNode:
render: function (createElement) { |
JSX
Si estás escribiendo muchas funciones render
, puede que le resulte doloroso escribir algo como esto:
createElement( |
Especialmente cuando la versión de la plantilla es tan simple en comparación:
<anchored-heading :level="1"> |
Es por eso que hay una librería Babel para utilizar JSX con Vue, lo que nos permite volver a una sintaxis más cercana a las plantillas:
import AnchoredHeading from './AnchoredHeading.vue' |
Generar el alias createElement
a h
es una convención común que verá en el ecosistema Vue y en realidad es necesaria para JSX. Si h
no está disponible en el alcance, su aplicación arrojará un error.
Para obtener más información sobre cómo JSX se asigna a JavaScript, consulte la documentación de uso.
Componentes Funcionales
El componente de encabezado anclado que creamos anteriormente es relativamente simple. No gestiona ningún estado, observe cómo no se le pasa ningún estado y no tiene métodos de ciclo de vida. En realidad, es solo una función con algunas propiedades.
En casos como este, podemos marcar los componentes como “funcionales” (functional
), lo que significa que no tienen estado (no hay datos reactivos) y sin instancia (no hay contexto this
). Un componente funcional se ve así:
Vue.component('my-component', { |
Nota: en las versiones anteriores a 2.3.0, la opción
props
es necesaria si desea aceptar props en un componente funcional. En 2.3.0+ puede omitir la opciónprops
y todos los atributos encontrados en el nodo componente se extraerán implícitamente como props.
En 2.5.0+, si está utilizando componentes de un solo archivo, los componentes funcionales basados en plantillas se pueden declarar con:
<template functional> |
Todo lo que necesita el componente se pasa a través de contexto
, que es un objeto que contiene:
props
: Un objeto de las propiedades proporcionadaschildren
: Un arreglo de los VNode hijosslots
: Una función retornando un objeto slotsdata
: El objeto entero objeto data, pasado al componente como segundo argumento decreateElement
parent
: Una referencia al componente padrelisteners
: (2.3.0+) Un objeto que contiene listeners de eventos registrados por los padres. Este es un alias dedata.on
injections
: (2.3.0+) si está usando la opcióninject
esto contendrá inyecciones resueltas.
Después de agregar function: true
, actualizar la función de procesamiento de nuestro componente de encabezado anclado requeriría agregar el argumentocontext
, actualizar this.$Slots.default
acontext.children
, luego actualizar this.level
a context.props.level
.
Dado que los componentes funcionales son solo funciones, son mucho más baratos de representar. Sin embargo, la falta de una instancia persistente significa que no se mostrarán en el árbol de componentes Vue devtools.
También son muy útiles como componentes de envoltura. Por ejemplo, cuando necesite:
- Elegir programáticamente uno de varios otros componentes para delegar a
- Manipular hijos, propiedades o datos antes de pasarlos a un componente hijo.
Aquí hay un ejemplo de un componente de lista-inteligente
(smart-list
) que delega en componentes más específicos, dependiendo de las propiedades que se le pasen:
var EmptyList = { /* ... */ } |
Pasar atributos y eventos a elementos/componentes hijos
En los componentes normales, los atributos no definidos como propiedades se agregan automáticamente al elemento raíz del componente, reemplazando o fusionando inteligentemente con cualquier atributo existente del mismo nombre.
Sin embargo, los componentes funcionales requieren que se defina explícitamente este comportamiento:
Vue.component('my-functional-button', { |
Al pasar context.data
como segundo argumento a createElement
, estamos transmitiendo los atributos o listeners de eventos utilizados en my-function-button
. De hecho, es tan transparente que los eventos ni siquiera requieren el modificador .native
.
Si está utilizando componentes funcionales basados en plantillas, también tendrá que agregar atributos y listeners manualmente. Ya que tenemos acceso a los contenidos de contexto individuales, podemos usar data.attrs
para pasar cualquier atributo HTML y listeners
(el alias para data.on
) para pasar a lo largo de cualquier evento.
<template functional> |
slots()
vs children
Quizás se pregunte por qué necesitamos tanto slots()
como children
. ¿No sería slots().default
igual que children
? En algunos casos, sí, pero ¿qué sucede si tiene un componente funcional con los siguientes hijos?
<my-functional-component> |
Para este componente, children
le dará ambos párrafos,slots().default
le dará solo el segundo, y slots ().foo
le dará solo el primero. Por lo tanto, tener children
y slots()
le permite elegir si este componente conoce un sistema de tragamonedas o tal vez delegue esa responsabilidad a otro componente al transmitir children
.
Compilación de plantillas
Tal vez le interese saber que las plantillas de Vue realmente se compilan para representar funciones. Este es un detalle de implementación que normalmente no necesita conocer, pero si desea ver cómo se compilan las funcionalidades específicas de la plantilla, puede que le resulte interesante. A continuación se muestra una pequeña demostración que usa Vue.compile
para compilar en vivo una cadena de plantilla
{{ result.render }}
_m({{ index }}): {{ fn }}
{{ result.staticRenderFns }}
{{ result }}