Aunque Vue.js recomienda el uso de plantillas (templates) para la mayoría de los casos, existen escenarios complejos donde la flexibilidad de JavaScript es necesaria. Aquí es donde entran en juego JSX y las funciones de renderizado.
Configuración del entorno
Para habilitar el soporte de JSX en un proyecto de Vue 2, es necesario instalar y configurar los complementos de Babel correspondientes:
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
La función render y createElement
En el núcleo del renderizado de Vue se encuentra la función createElement (frecuentemente abreviada como h). Esta función permite construir el DOM virtual programáticamente.
// Estructura básica de una función render
render(h) {
return h('h2', this.tituloPagina);
}
// Ejemplo avanzado con atributos y elementos anidados
render(h) {
return h('div', {
class: 'contenedor-notificaciones',
style: {
padding: '20px',
backgroundColor: '#f0f0f0'
}
}, [
h('span', {
domProps: {
innerText: 'Mensaje del sistema:'
},
style: {
fontWeight: 'bold'
}
}),
h(ComponenteAlerta, {
props: {
tipo: 'error'
}
})
]);
}
El código anterior generaría una estructura equivalente a este HTML:
<div class="contenedor-notificaciones" style="padding: 20px; background-color: #f0f0f0;">
<span style="font-weight: bold;">Mensaje del sistema:</span>
<!-- ComponenteAlerta renderizado aquí -->
</div>
Uso de Slots en JSX
El manejo de slots cambia significativamente cuando pasamos de plantillas a JSX. Mientras que en los componentes estándar usamos $slots, para una mayor flexibilidad (especialmente con datos dinámicos) recurrimos a $scopedSlots.
Slots con Sintaxis JSX (Babel)
Si utilizamos el plugin de Babel, podemos definir los slots de forma más declarativa dentro del objeto scopedSlots.
// Componente Padre
const VistaPrevia = {
render() {
return (
<ContenedorBase
scopedSlots={{
cabecera: (props) => (
<div class="header">Titulo: {props.info.titulo}</div>
),
cuerpo: (props) => (
<p>Contenido detallado: {props.info.descripcion}</p>
)
}}
>
</ContenedorBase>
);
}
};
Slots mediante createElement
Si preferimos no usar la sintaxis de etiquetas XML dentro de JavaScript, podemos usar el objeto de datos de createElement:
render(h) {
return h(ComponenteLista, {
scopedSlots: {
elemento: (datos) => {
return h('li', `Usuario: ${datos.nombre}`);
},
pie: () => h('footer', 'Fin de la lista')
}
});
}
Componentes Funcionales
Los componentes funcionales son una alternativa ligera para componentes que no requieren estado interno (data) ni métodos de ciclo de vida. Se caracterizan por ser simplemente una función que recibe un contexto.
Características principales:
- Sin instancia (this): No tienen acceso a
this. Toda la información se recibe a través del segundo argumento:context. - Rendimiento: Al no requerir la creación de una instancia de Vue, su renderizado es mucho más rápido.
- Sin estado: No pueden tener datos reactivos propios ni
computed properties.
// Definición de un componente funcional
export default {
functional: true,
props: ['etiqueta', 'valor'],
render(h, context) {
const { props, listeners } = context;
return h('button', {
on: {
click: listeners.click // Se accede a los eventos mediante context.listeners
},
class: 'btn-personalizado'
}, `${props.etiqueta}: ${props.valor}`);
}
};
Este tipo de componentes es ideal para elementos pruamente visuales o envoltorios lógicos que transforman props antes de pasarlas a otros componentes.