Vue js - v-for 循环中的 ref 始终返回 null

发布于 2025-01-12 20:27:50 字数 697 浏览 1 评论 0原文

我正在尝试访问 Vue 中的一个元素,因为我想查看它的类何时更改(我正在使用 Bootstrap,所以我想监视类何时更改为显示)。问题是,一旦我将 ref'd 元素放入 v-for 循环中,它总是返回 null。我尝试将引用的名称附加到循环项的索引,然后尝试将其记录到控制台,但它总是返回“null”。

一旦我将元素从 v-for 循环中取出,它就会很好地返回该元素。

我似乎找不到解决办法。这是 Vue 根本不支持的东西吗?如果没有,是否有更好的方法来观察 DOM 以查看元素的类是否发生变化?

PS 我正在使用组合 API。

模板:

<template>
    <div v-for="room in rooms" :key="room.id" class="">
        <p class="test" :ref="'test'+room.id">Hello world</p>
    </div>
</template

脚本:

setup(){
      const test1 = ref(null)

      onMounted(() => {
          console.log("test1: ", test1.value)
      })

      return { test1 }
}

I am trying to access an element in Vue as I want to see when it's class changes (I'm using Bootstrap so I want to watch for when a class changes to shown). The problem is that as soon as I put a ref'd element into a v-for loop it always returns null. I have tried appending the ref's name with the index of the loop item, and then trying to log this to the console it just always returns 'null'.

As soon as I take the element out of the v-for loop it returns the element fine.

I can't seem to find a solution. Is this something that Vue just simply doesn't support? If not, is there a better way for me to watch the DOM to see if an element's class changes?

P.S. I'm using the composition API.

Template:

<template>
    <div v-for="room in rooms" :key="room.id" class="">
        <p class="test" :ref="'test'+room.id">Hello world</p>
    </div>
</template

Script:

setup(){
      const test1 = ref(null)

      onMounted(() => {
          console.log("test1: ", test1.value)
      })

      return { test1 }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

っ〆星空下的拥抱 2025-01-19 20:27:50

这看起来是一个非常常见的问题,所以我深入研究了 Vue 源代码并发现了以下内容:

这是 Vue 更新和渲染方式的限制。如果同一个模板有多个 s,那么每个模板都有自己的补丁队列。每个补丁队列都按照自己的时间表进行处理,因此一个补丁队列有可能在另一个队列开始之前完成。如果发生这种情况,则排队的更新之一将不会发生。这意味着 Vue 不会检测到元素的更改,因此根本不会进行任何重新渲染,这就是为什么你的元素不会显示在控制台输出中!

您可以通过确保所有更新队列在彼此开始之前完成来解决此问题,我们可以通过向相关组件添加 update: {sync: true } 配置来解决此问题。这将迫使 Vue 在处理应用程序中的任何其他更改之前同步处理所有更新队列。

您还可以将 sync: true 配置添加到父组件中,以包装所有其他组件并确保它们全部同步。

<script>
  setup() {
    const test1 = ref(null)

    onMounted(() => {
      console.log("test1: ", test1.value)
    })

    return { test1 }
  }

  render() {
      return (
        <div>Hello World</div>
      )}

  </script>

This looks like a pretty common problem, so I went digging through the Vue source and found the following:

It's a limitation of the way Vue does updates and renders. If you have multiple s for the same template, then each of them has its own patch queue. Each patch queue gets processed on its own schedule, so it's possible for one to finish before another even starts. If that happens, then one of the queued updates won't happen. That would mean Vue wouldn't detect changes to your element and hence wouldn't do any re-rendering at all, which is why your element doesn't show up in your console output!

You can work around this by making sure all update queues finish before each other begins, which we can do by adding an update: { sync: true } config to your component in question. This will force Vue to process all of your update queues synchronously before processing any other changes in the application.

You could also add a sync: true config to your parent's component that wraps all of your other components and make sure they all sync.

<script>
  setup() {
    const test1 = ref(null)

    onMounted(() => {
      console.log("test1: ", test1.value)
    })

    return { test1 }
  }

  render() {
      return (
        <div>Hello World</div>
      )}

  </script>

太阳哥哥 2025-01-19 20:27:50

v-for 中的模板引用

存在一个已知错误 (vuejs/core#5525),其中数组引用未正确填充。

一种解决方法是绑定一个将元素引用推入数组的函数:

                       

Template refs in a v-for

There's a known bug (vuejs/core#5525), where array refs are not properly populated.

One workaround is to bind a function that pushes the element refs into the array:

                       ????
<p class="test" :ref="el => roomRefs.push(el)">Hello world</p>

demo 1

Watching class changes

To observe changes to the class attribute of the elements, use a MutationObserver.

mounted hook: For each roomRefs item:

  1. Create a MutationObserver with a callback that handles mutations (i.e., a change to the observed attribute).

  2. Use that MutationObserver instance to observe the class attribute on the roomRefs item.

  3. Collect each observer into an array, as we'll need to disconnect the observer upon unmounting.

unmounted hook:

  1. disconnect all observers to stop watching for changes. This cleanup step is necessary to avoid a memory leak.
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()))
  }
}

demo 2

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文