在 Vue 3 中,如果你在一个通过 v-for 渲染的元素或组件上直接使用 ref,Vue 会自动将它们收集成一个数组形式的 ref,而不是单个实例。
示例代码:
html
<template>
<div v-for="item in list" :key="item.id" ref="rows">
{{ item.name }}
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const list = ref([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
])
const rows = ref([])
onMounted(() => {
console.log(rows.value)
// => [HTMLDivElement, HTMLDivElement, HTMLDivElement]
})
</script>
这段代码在 Vue 3 中虽然看似没问题,但存在内存泄漏风险 ------因为直接使用 ref="rows" 在 v-for 内时,Vue 不会在卸载或列表项变化时自动清空旧引用。
进阶写法:
html
<template>
<div
v-for="item in list"
:key="item.id"
:ref="el => setRowRef(el, item.id)"
>
{{ item.name }}
</div>
</template>
<script setup>
import { reactive, nextTick, onBeforeUnmount } from 'vue'
const list = ref([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
])
// 用对象代替数组存储 refs,按 id 映射更稳定
const rowRefs = reactive({})
function setRowRef(el, id) {
if (el) {
rowRefs[id] = el
} else {
// Vue 在节点卸载时会传入 null,这里主动清理引用
delete rowRefs[id]
}
}
// 可选:组件卸载时彻底清空引用,防止残留
onBeforeUnmount(() => {
for (const key in rowRefs) delete rowRefs[key]
})
</script>