Existe una analogía popular de Vue que dice así: Vue es lo que obtienes cuando React y Angular se unen y hacen un bebé. Siempre he compartido este sentimiento. Con la pequeña curva de aprendizaje de Vue, no es de extrañar que a tanta gente le guste. Dado que Vue intenta darle al desarrollador poder sobre los componentes y su implementación tanto como sea posible, este sentimiento ha llevado al tema de hoy.
El termino componentes sin render se refiere a componentes que no representan nada. En este artículo, cubriremos cómo Vue maneja la renderización de un componente.
También veremos cómo podemos usar el render()
función para construir componentes sin renderizado.
Es posible que desee saber un poco sobre Vue para aprovechar al máximo este artículo. Si eres un novato, Sarah Drasner te respalda. La documentación oficial también es un recurso muy bueno.
Desmitificando cómo Vue renderiza un componente
Vue tiene varias formas de definir el marcado de un componente. Hay:
- Componentes de un solo archivo que nos permiten definir componentes y su marcado como lo haríamos con un archivo HTML normal.
- El
template
propiedad del componente que nos permite usar los literales de plantilla de JavaScript para definir el marcado de nuestro componente. - El
el
La propiedad del componente le dice a Vue que consulte el DOM para obtener un marcado para usar como plantilla.
Probablemente hayas escuchado (posiblemente bastante molesto): al final del día, Vue y todos sus componentes son solo JavaScript. Pude ver por qué puede pensar que esa declaración está mal con la cantidad de HTML y CSS que escribimos. Caso y punto: componentes de un solo archivo.
Con componentes de un solo archivo, podemos definir un componente de Vue como este:
<template>
<div class="mood">
{{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }}
</div>
</template>
<script>
export default {
data: () => ({ todayIsSunny: true })
}
</script>
<style>
.mood:after {
content: '🎉🎉';
}
</style>
¿Cómo podemos decir que Vue es “solo JavaScript” con todo ese galimatías arriba? Pero, al final del día, lo es. Vue intenta facilitar que nuestras vistas administren su estilo y otros activos, pero Vue no lo hace directamente, lo deja en manos del proceso de compilación, que probablemente sea un paquete web.
Cuando el paquete web encuentra un .vue
archivo, lo ejecutará a través de un proceso de transformación. Durante este proceso, el CSS se extrae del componente y se coloca en su propio archivo, y el contenido restante del archivo se transforma en JavaScript. Algo como esto:
export default {
template: `
<div class="mood">
{{ todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me' }}
</div>`,
data: () => ({ todayIsSunny: true })
}
Bueno … no exactamente lo que tenemos arriba. Para comprender qué sucede a continuación, debemos hablar sobre el compilador de plantillas.
El compilador de plantillas y la función de renderizado
Esta parte del proceso de compilación de un componente de Vue es necesaria para compilar y ejecutar todas las técnicas de optimización que Vue implementa actualmente.
Cuando el compilador de plantillas encuentra esto:
{
template: `<div class="mood">...</div>`,
data: () => ({ todayIsSunny: true })
}
… extrae el template
propiedad y compila su contenido en JavaScript. Luego se agrega una función de renderizado al objeto componente. Esta función de representación, a su vez, devolverá el contenido de la propiedad de la plantilla extraída que se convirtió en JavaScript.
Así es como se verá la plantilla anterior como función de renderizado:
...
render(h) {
return h(
'div',
{ class: 'mood' },
this.todayIsSunny ? 'Makes me happy' : 'Eh! Doesn't bother me'
)
}
...
Consulte la documentación oficial para obtener más información sobre la función de renderizado.
Ahora, cuando el objeto del componente se pasa a Vue, la función de renderizado del componente pasa por algunas optimizaciones y se convierte en un VNode (nodo virtual). VNode es lo que se pasa a snabbdom (la biblioteca que Vue usa internamente para administrar el DOM virtual). Sarah Drasner hace un buen trabajo explicando la “h” en la función de renderizado anterior.
Un VNode es la forma en que Vue renderiza los componentes. Por cierto, ¡la función de render también nos permite usar JSX en Vue!
Tampoco tenemos que esperar a que Vue agregue la función de renderizado por nosotros; podemos definir una función de renderizado y debería tener prioridad sobre el
o la template
propiedad. Lea aquí para aprender sobre la función de renderizado y sus opciones.
Al construir sus componentes de Vue con Vue CLI o algún proceso de compilación personalizado, no tiene que importar el compilador de plantillas que puede aumentar el tamaño de su archivo de compilación. Sus componentes también están optimizados previamente para un rendimiento brillante y archivos JavaScript realmente livianos.
Entonces … Componentes Renderless Vue
Como mencioné, el término componentes sin render significa componentes que no representan nada. ¿Por qué querríamos componentes que no rendericen nada?
Podemos atribuirlo a la creación de una abstracción de la funcionalidad del componente común como su propio componente y extender dicho componente para crear componentes mejores y aún más robustos. Además, SÓLIDO
Según el principio de responsabilidad única de SOLID:
Una clase solo debe tener un propósito.
Podemos trasladar ese concepto al desarrollo de Vue haciendo que cada componente tenga una sola responsabilidad.
Puedes ser como Nicky y “pfft, sí, lo sé”. ¡Bien, seguro! Su componente puede tener el nombre “entrada de contraseña” y seguramente presenta una entrada de contraseña. El problema con este enfoque es que cuando desee reutilizar este componente en otro proyecto, es posible que tenga que ir al código fuente del componente para modificar el estilo o el marcado para mantenerse en línea con la guía de estilo del nuevo proyecto.
Esto rompe una regla de SÓLIDO conocida como el principio abierto-cerrado el cual establece que:
Una clase o un componente, en este caso, debe estar abierto para extensión, pero cerrado para modificación.
Esto quiere decir que en lugar de editar la fuente del componente, debería poder extenderlo.
Dado que Vue comprende los principios SOLID, permite que los componentes tengan accesorios, eventos, ranuras y ranuras con alcance, lo que facilita la comunicación y extensión de un componente. Luego podemos construir componentes que tengan todas las características, sin ningún estilo o marcado. Lo cual es realmente bueno para la reutilización y el código eficiente.
Cree un componente sin renderizado “Alternar”
Esto será sencillo. No es necesario configurar un proyecto CLI de Vue.
El componente Alternar le permitirá alternar entre los estados activado y desactivado. También le proporcionará ayudantes que le permitirán hacer eso. Es útil para crear componentes como, bueno, componentes de encendido / apagado, como casillas de verificación personalizadas y cualquier componente que necesite un estado de encendido / apagado.
Apliquemos rápidamente nuestro componente: diríjase a la sección JavaScript de un lápiz CodePen y sígalo.
// toggle.js
const toggle = {
props: {
on: { type: Boolean, default: false }
},
render() {
return []
},
data() {
return { currentState: this.on }
},
methods: {
setOn() {
this.currentState = true
},
setOff() {
this.currentState = false
},
toggle() {
this.currentState = !this.currentState
}
}
}
Esto es bastante mínimo y aún no está completo. Necesita una plantilla, y como no queremos que este componente represente nada, tenemos que asegurarnos de que funcione con cualquier componente que lo haga.
¡Cue las tragamonedas!
Ranuras en componentes sin renderizado
Las ranuras nos permiten colocar contenido entre las etiquetas de apertura y cierre de un componente de Vue. Como esto:
<toggle>
This entire area is a slot.
</toggle>
En los componentes de un solo archivo de Vue, podríamos hacer esto para definir una ranura:
<template>
<div>
<slot/>
</div>
</template>
Bueno, para hacer eso usando un render()
función, podemos:
// toggle.js
render() {
return this.$slots.default
}
Podemos colocar cosas automáticamente dentro de nuestro componente de alternancia. Sin marcado, nada.
Envío de datos al árbol con ranuras con alcance
En toggle.js
, tuvimos algunos on
estado y algunas funciones auxiliares en el methods
objeto. Sería bueno si pudiéramos darles acceso al desarrollador. Actualmente estamos usando ranuras y no nos permiten exponer nada del componente secundario.
Lo que queremos es ranuras con alcance. Las ranuras con alcance funcionan exactamente como las ranuras con la ventaja adicional de que un componente tiene una ranura con alcance que puede exponer datos sin disparar eventos.
Podemos hacer esto:
<toggle>
<div slot-scope="{ on }">
{{ on ? 'On' : 'Off' }}
</div>
</toggle>
Ese slot-scope
atributo que puedes ver en el div
está desestructurando un objeto expuesto desde el componente de alternancia.
Volviendo al render()
función, podemos hacer:
render() {
return this.$scopedSlots.default({})
}
Esta vez, llamamos a la propiedad predeterminada en el $scopedSlots
object como método porque los slots con ámbito son métodos que toman un argumento. En este caso, el nombre del método es predeterminado, ya que no se le dio un nombre a la ranura con ámbito y es la única ranura con ámbito que existe. El argumento que pasamos a una ranura con ámbito se puede exponer desde el componente. En nuestro caso, expongamos el estado actual de on
y los ayudantes para ayudar a manipular ese estado.
render() {
return this.$scopedSlots.default({
on: this.currentState,
setOn: this.setOn,
setOff: this.setOff,
toggle: this.toggle,
})
}
Uso del componente de alternancia
Podemos hacer todo esto en CodePen. Esto es lo que estamos construyendo:
Vea el Pen ZRaYWm de Samuel Oloruntoba (@ kayandrae07) en CodePen.
Aquí está el marcado en acción:
<div id="app">
<toggle>
<div slot-scope="{ on, setOn, setOff }" class="container">
<button @click="click(setOn)" class="button">Blue pill</button>
<button @click="click(setOff)" class="button isRed">Red pill</button>
<div v-if="buttonPressed" class="message">
<span v-if="on">It's all a dream, go back to sleep.</span>
<span v-else>I don't know how far the rabbit hole goes, I'm not a rabbit, neither do I measure holes.</span>
</div>
</div>
</toggle>
</div>
- Primero, estamos desestructurando el estado y los ayudantes de la ranura delimitada.
- Luego, dentro de la ranura con alcance, creamos dos botones, uno para activar el estado actual y el otro para desactivarlo.
- El
click
El método está ahí para asegurarse de que se haya presionado un botón antes de mostrar un resultado. Puede consultar el método de clic a continuación.
new Vue({
el: '#app',
components: { toggle },
data: {
buttonPressed: false,
},
methods: {
click(fn) {
this.buttonPressed = true
fn()
},
},
})
Todavía podemos pasar accesorios y disparar eventos desde el componente Toggle. El uso de una ranura con alcance no cambia nada.
new Vue({
el: '#app',
components: { toggle },
data: {
buttonPressed: false,
},
methods: {
click(fn) {
this.buttonPressed = true
fn()
},
},
})
Este es un ejemplo básico, pero podemos ver cuán poderoso puede llegar a ser cuando comenzamos a construir componentes como un selector de fechas o un widget de autocompletar. Podemos reutilizar estos componentes en varios proyectos sin tener que preocuparnos de que esas molestas hojas de estilo se interpongan en el camino.
Una cosa más que podemos hacer es exponer los atributos necesarios para la accesibilidad desde la ranura con alcance y tampoco tenemos que preocuparnos por hacer accesibles los componentes que extienden este componente.
En resumen
- La función de renderizado de un componente es increíblemente poderosa.
- Cree sus componentes de Vue para un tiempo de ejecución más rápido.
- Componente
el
,template
o incluso los componentes de un solo archivo se compilan en funciones de renderizado. - Intente construir componentes más pequeños para un código más reutilizable.
- Su código no necesita ser SÓLIDO, pero es una muy buena metodología para codificar.
Fuentes
- Desmitificando los componentes internos de Vue.js
- Ranuras para componentes de Vue
- El truco para comprender las ranuras con alcance en Vue.js por Adam Wathan
- Componentes sin renderizado en Vue.js por Adam Wathan