Estoy tratando de acceder a un elemento en Vue porque quiero ver cuándo cambia su clase (estoy usando Bootstrap, así que quiero ver cuándo cambia una clase para mostrar). El problema es que tan pronto como pongo un elemento ref en un bucle v-for, siempre devuelve nulo. Intenté agregar el nombre de la referencia con el índice del elemento de bucle, y luego intenté registrar esto en la consola, siempre devuelve 'nulo'.
Tan pronto como saco el elemento del bucle v-for, devuelve el elemento bien.
Parece que no puedo encontrar una solución. ¿Es esto algo que Vue simplemente no admite? Si no, ¿hay una mejor manera de ver el DOM para ver si cambia la clase de un elemento?
PD: estoy usando la API de composición.
Modelo:
<template> <div v-for="room in rooms" :key="room.id" class=""> <p class="test" :ref="'test'+room.id">Hello world</p> </div> </templateGuion:
setup(){ const test1 = ref(null) onMounted(() => { console.log("test1: ", test1.value) }) return { test1 } }v-for Hay un error conocido ( vuejs/core#5525 ), donde las referencias de matriz no se completan correctamente.
Una solución consiste en vincular una función que inserte las referencias del elemento en la matriz:
👇 <p class="test" :ref="el => roomRefs.push(el)">Hello world</p>class Para observar cambios en el atributo de class de los elementos, use un MutationObserver .
gancho mounted : Para cada roomRefs artículo:
Cree un MutationObserver con una devolución de llamada que maneje las mutaciones (es decir, un cambio en el atributo observado).
Utilice esa instancia de MutationObserver para observe el atributo de class en el elemento roomRefs .
Reúna a cada observador en una matriz, ya que necesitaremos desconectar el observador al desmontarlo.
gancho unmounted :
disconnect todos los observadores para dejar de observar los cambios. Este paso de limpieza es necesario para evitar una pérdida de memoria. export default { props: { rooms: { type: Array, required: true, }, }, setup() { const roomRefs = ref([]) let observers = [] onMounted(() => { 1️⃣ observers = roomRefs.value.map(roomEl => { const observer = new MutationObserver(mutations => { for (const m of mutations) { const newValue = m.target.getAttribute(m.attributeName) console.log('class changed', { roomEl, newValue, oldValue: m.oldValue }) } }) 2️⃣ observer.observe(roomEl, { attributes: true, attributeOldValue: true, attributeFilter: ['class'], }) 3️⃣ return observer }) }) 4️⃣ onUnmounted(() => observers.forEach(obs => obs.disconnect())) } }Esto parece un problema bastante común, así que busqué en la fuente de Vue y encontré lo siguiente:
Es una limitación de la forma en que Vue realiza actualizaciones y renderizaciones. Si tiene varios correos electrónicos para la misma plantilla, cada uno de ellos tiene su propia cola de parches. Cada cola de parches se procesa en su propio horario, por lo que es posible que uno termine antes de que comience otro. Si eso sucede, entonces una de las actualizaciones en cola no se realizará. Eso significaría que Vue no detectaría cambios en su elemento y, por lo tanto, no volvería a renderizar en absoluto, ¡por lo que su elemento no aparece en la salida de su consola!
Puede solucionar esto asegurándose de que todas las colas de actualización finalicen antes de que comiencen las demás, lo que podemos hacer agregando una update: { sync: true } configuración a su componente en cuestión. Esto obligará a Vue a procesar todas sus colas de actualización de forma sincrónica antes de procesar cualquier otro cambio en la aplicación.
También puede agregar una sync: true al componente de su padre que envuelve todos sus otros componentes y se asegura de que todos se sincronicen.
<script> setup() { const test1 = ref(null) onMounted(() => { console.log("test1: ", test1.value) }) return { test1 } } render() { return ( <div>Hello World</div> )} </script>