v-for循环内的Vue.js ref

Lev*_*hev 9 javascript vue.js vue-component vuejs2

我尝试在v-for循环内使用组件,并初始化ref以便将来从父级访问这些组件的某些方法。这是我的情况的简化代码:

<template>
    <div class="hello">
        {{ msg }}
        <ul>
            <list-item 
                v-for="item in items" 
                :key="item.id" 
                :value="item.text" 
                :ref="`item${item.id}`"
            />
        </ul>
    </div>
</template>

<script>
    import ListItem from "./ListItem";
    export default {
        name: "HelloWorld",
        components: {
            ListItem
        },
        data() {
            return {
                msg: "Welcome to Your Vue.js App",
                items: [
                    { id: 1, text: "foo" },
                    { id: 2, text: "bar" },
                    { id: 3, text: "baz" },
                    { id: 4, text: "foobar" }
                ]
            };
        },
        mounted() {
            setTimeout(() => this.$refs.item2.highlight(), 1500);
        }
    };
</script>
Run Code Online (Sandbox Code Playgroud)

ListItem组件:

<template>
    <li v-bind:class="{ highlight: isHighlighted }">
        {{value}}
    </li>
</template>

<script>
    export default {
        name: "list-item",
        props: ["value"],
        data() {
            return {
                isHighlighted: false
            };
        },
        methods: {
            highlight() {
                this.isHighlighted = !this.isHighlighted;
            }
        }
    };
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .highlight {
        color: red;
    }
</style>
Run Code Online (Sandbox Code Playgroud)

它只是呈现了一些列表项,并在半秒后突出显示其中一个。但是我遇到一个错误:Uncaught TypeError: _this.$refs.item2.highlight is not a function
在调试会话后,我发现了一个有趣的事实:v-for循环内定义的引用不是组件,而是具有一个组件的数组。
逻辑是什么,f包装器是什么?有人遇到这种情况吗?有人可以解释这种行为吗?
上面提供的代码可以与“ setTimeout(() => this.$refs.item2[0].highlight(), 1500);
我必须始终通过”一起使用[0]吗?有没有更好的方法?请帮忙。

Alo*_*nad 10

我试图通过从方法传递索引来处理 v-for 中的引用:

<div v-for="(item, index) in items" @click="toggle(index)">
  <p ref="someRef"></p>
</div>

toggle(index) {
  this.refs['someRef'][index].toggle();
}
Run Code Online (Sandbox Code Playgroud)

但实际上它切换了错误的元素,因为 refs 的索引没有排序。

所以我所做的是向 ref 元素添加 data 属性:

<div v-for="(item, index) in items" @click="toggle(index)">
  <p ref="someRef" :data-key="index"></p>
</div>
Run Code Online (Sandbox Code Playgroud)

现在每个 ref 都有其特定的数据键。并且可以像这样切换:

toggle(index) {
  const dropdown = this.$refs['someRef'].find(
        el => el.$attrs['data-key'] === index
    );
  dropdown.toggle();
}
Run Code Online (Sandbox Code Playgroud)

  • 这个方法在vue3中会被制动。因此,如果您使用 vue3 或更高版本,我会避免这种情况 - https://v3.vuejs.org/guide/migration/array-refs.html#frontmatter-title (2认同)

小智 9

当使用带v-for的引用时,组件/ DOM节点直接作为数组存储在变量名中,因此您无需在引用名中使用索引号。因此,您可以执行以下操作:

<list-item
  v-for="item in items" 
  :key="item.id" 
  :value="item.text" 
  ref="items"
/>
Run Code Online (Sandbox Code Playgroud)

并像这样使用组件中的引用:

this.$refs.items[index]
Run Code Online (Sandbox Code Playgroud)

还要注意,裁判可能没有顺序,需要以不同的方式处理,这是完全不同的问题。您可以在这里关注:https : //github.com/vuejs/vue/issues/4952

  • 请注意,这在 Vue 3 中不起作用 (10认同)

Sye*_*yed 7

对于 Vue 3 用户:

在 Vue 3 中,这种用法将不再自动在$refs. 要从单个绑定中检索多个引用,请绑定ref到一个提供更多灵活性的函数(这是一个新功能):

HTML

<div v-for="item in list" :ref="setItemRef"></div>
Run Code Online (Sandbox Code Playgroud)

使用选项 API:

export default {
  data() {
    return {
      itemRefs: []
    }
  },
  methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el)
      }
    }
  },
  beforeUpdate() {
    this.itemRefs = []
  },
  updated() {
    console.log(this.itemRefs)
  }
}
Run Code Online (Sandbox Code Playgroud)

使用组合 API:

import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }
    onBeforeUpdate(() => {
      itemRefs = []
    })
    onUpdated(() => {
      console.log(itemRefs)
    })
    return {
      setItemRef
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是文档链接:https : //v3.vuejs.org/guide/migration/array-refs.html

  • 根据新文档,此功能从 3.2.25 开始在 vue 3 中再次可用 https://staging.vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for (5认同)
  • 请添加一行关于如何使用 ref 的内容,例如关注某个元素,引用其 ref (2认同)

小智 6

我遇到了同样的问题。

正如 sobolevon 提到的, 的返回值$refs.{ref name}是 v-for refs 中的一个数组,所以我的解决方案是考虑$refs.{ref name}默认情况下只有一个项目的数组,并 write $refs.{ref name}[0].methodToCall()

它适用于我的情况。


Vis*_*sie 5

对于任何使用 Vue 3 和 Typescript 的人来说,这个 ( vuejs/core#5525 ) 问题仍然悬而未决。根据其他答案,您可以执行以下操作:

更新: vuejs/core#5525似乎已修复,因此不同的解决方案可能会更好。

<div
   v-for="item in items"
   :ref="addRef"
   ...
</div>

...

function addRef(el: unknown) {
  if (el instanceof Element) {
    participantRefs.value.push(el);
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

在使用Vue 3的@Syed答案的基础上,您遇到了此处所述的问题https://vuejs.org

需要注意的是,ref 数组不保证与源数组的顺序相同。

我遇到了一个问题,我需要渲染的列表与引用列表具有奇偶性。这就是我为解决这个问题所做的:

<script setup>
  import { ref } from 'vue'
  import Comp from './Comp.vue'

  const list = ref([
    {
      name: 'Stripe',
      ref: null,
    },
    {
      name: 'Default',
      ref: null,
    }
  ]);

  function setItemRef(el, idx) {
    if (el) {
      list.value[idx].ref = el;
    }
  }
</script>

<template>
  <ul>
    <li v-for="(item, idx) in list">
      <Comp :ref="(el) => setItemRef(el, idx)"/>
      {{item}}
    </li>
  </ul>
</template>
Run Code Online (Sandbox Code Playgroud)

以下是在 SFC 中运行的示例: https: //sfc.vuejs.org