Estoy tratando de configurar un componente con una ranura, que cuando se procesa agrega una clase a cada hijo de esa ranura. De una manera muy simplificada:
<template> <div> <slot name="footerItems"></slot> </div> </template> ¿Cómo haría esto? Mi solución actual es agregar la clase a los elementos en un gancho onBeforeUpdate :
<script setup lang="ts"> import { useSlots, onMounted, onBeforeUpdate } from 'vue'; onBeforeUpdate(() => addClassToFooterItems()); onMounted(() => addClassToFooterItems()); function addClassToFooterItems() { const slots = useSlots(); if (slots && slots.footerItems) { for (const item of slots.footerItems()) { item.el?.classList.add("card-footer-item"); } } } </script> Sin embargo, los elementos pierden el estilo cada vez que se vuelven a renderizar (usando npm run serve ) y también las pruebas de broma me dan una advertencia:
[Vue warn]: Slot "footerItems" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.¿Debo mover la ranura a su propio componente y usar una función de renderizado allí? Pero incluso entonces no estoy seguro de cómo editar los elementos secundarios para agregar las clases o cómo producir varios elementos de nivel raíz a partir de la función de representación.
Por lo tanto, me las arreglé para resolver esto de una manera increíblemente hacky, pero al menos mi problema con el renderizado ya no ocurre y la broma no se queja. Escribí un componente con una función de representación que agrega esa clase a todos los niños
<template> <render> <slot></slot> </render> </template> <script setup lang="ts"> import { useSlots } from 'vue'; const props = defineProps<{ childrenClass: string; }>(); function recurseIntoFragments(element: any): any { if (element.type.toString() === 'Symbol(Fragment)' && element.children[0].type.toString() === 'Symbol(Fragment)' ) { return recurseIntoFragments(element.children[0]); } else { return element; } } const render = () => { const slot = useSlots().default!(); recurseIntoFragments(slot[0]).children.forEach((element: any) => { if (element.props?.class && !element.props?.class.includes(props.childrenClass)) { element.props.class += ` ${props.childrenClass}`; } else { element.props.class = props.childrenClass; } }); return slot; } </script>Luego simplemente envolvería la ranura en este componente para agregar la clase a los elementos secundarios:
<template> <div> <classed-slot childrenClass="card-footer-item"> <slot name="footerItems"></slot> </classed-slot> </div> </template>Con mucho gusto aceptaría cualquier respuesta que mejore esta solución, especialmente:
any sienten raros, pero me parece muy poco práctico trabajar con tipos Vue para tragamonedas, ya que generalmente son uniones de 3 o 4 tipos y la única solución es envolverlos en controles de tipo. EDITAR Este es mi último intento, una función de representación en un archivo ClassedSlot.js :
import { cloneVNode } from 'vue'; function recursivelyAddClass(element, classToAdd) { if (Array.isArray(element)) { return element.map(el => recursivelyAddClass(el, classToAdd)); } else if (element.type.toString() === 'Symbol(Fragment)') { const clone = cloneVNode(element); clone.children = recursivelyAddClass(element.children, classToAdd) return clone; } else { return cloneVNode(element, { class: classToAdd }); } } export default { props: { childrenClass: { type: String, required: true }, }, render() { const slot = this.$slots.default(); return recursivelyAddClass(slot, this.$props.childrenClass); }, };El uso de este componente es exactamente el mismo que en el anterior. Estoy un poco contento con esta solución, parece más robusta e idiomática. Tenga en cuenta que es javascript porque me resultó muy difícil escribir estas funciones correctamente.
¿Necesitas la clase para aplicar algo de estilo?
Puedes pasar el estilo o la clase con accesorios .
Y aplica la clase o estilo con :style="" o :class="" , se explica aquí
Pero solo para aplicar una clase, solo :class="" resuelve tu problema