Vue3组合式API中v-for循环内使用getBoundingClientRect()的问题
Vue选项式API转组合式API获取元素位置问题
原选项式API代码
<template> <div> <div v-for="index in divs" :key="index"> <div :ref="'ref_' + index" @mouseover="divDragOver($event, index)"> This is div {{ index }} </div> </div> </div> </template> <script> export default { name: 'App', data: () => ({ divs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }), methods: { divDragOver(e, i) { let [div] = this.$refs['ref_' + i]; let top = div.getBoundingClientRect().top; console.log(top); }, }, }; </script>
尝试转换的组合式API代码(无法正常运行)
<template> <div> <div v-for="index in divs" :key="index"> <div :ref="'ref_' + index" @mouseover="divDragOver($event, index)"> This is div {{ index }} </div> </div> </div> </template> <script setup> import { ref } from 'vue' const divs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] function divDragOver(e, i) { let itemRefs = ['ref_' + i] let top = itemRefs.getBoundingClientRect().top console.log(top) } </script>
实际应用场景代码
<template> <li v-for="item in navigation" :key="item.name" class="px-1"> <Menu as="div" v-slot="{ open }" class="relative" :ref="(el) => (refs[item.name] = el)" > <MenuButton :class="[ item.current ? 'bg-gray-100' : 'hover:bg-gray-100', 'group flex h-10 w-full items-center justify-center rounded-md p-2.5 text-standard font-semibold leading-6 text-gray-700', ]" @click="getParentPosition($event, item.name)" > <span class="-mx-2.5 flex w-10 flex-none items-center justify-center" > <component :is="item.icon" class="h-5 w-5 shrink-0 text-gray-400" aria-hidden="true" /> </span> </MenuButton> <transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95" > <MenuItems class="fixed left-14 z-10 mt-1.5 w-32 origin-top-right rounded-md bg-white py-2 shadow-md ring-1 ring-gray-900/5 focus:outline-none" > <MenuItem v-for="subItem in item.children" :key="subItem.name" v-slot="{ active }" > <a :href="item.href" :class="[ active ? 'bg-gray-50' : '', 'block px-3 py-1 text-standard leading-6 text-gray-500', ]" > {{ subItem.name }} </a> </MenuItem> </MenuItems> </transition> </Menu> </li> </template> <script setup> import { ref } from 'vue' const refs = ref([]) function getParentPosition(e, k) { let itemRef = refs.value[k] console.log(itemRef) } </script>
解决方案
1. 基础示例的正确转换
组合式API中,动态生成的ref需要用响应式对象/数组存储,不能直接通过字符串访问。以下是修正后的代码:
<template> <div> <div v-for="index in divs" :key="index"> <!-- 用回调函数将元素存入响应式对象 --> <div :ref="el => { if (el) refs[index] = el }" @mouseover="divDragOver($event, index)"> This is div {{ index }} </div> </div> </div> </template> <script setup> import { ref } from 'vue' const divs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // 用响应式对象存储每个元素的ref const refs = ref({}) function divDragOver(e, i) { const div = refs.value[i] if (div) { const top = div.getBoundingClientRect().top console.log(top) } } </script>
2. 实际场景的修正
实际场景中,item.name是字符串类型的键,用数组存储ref不合适,应改为响应式对象,同时注意组件卸载时的null值处理:
<template> <li v-for="item in navigation" :key="item.name" class="px-1"> <Menu as="div" v-slot="{ open }" class="relative" <!-- 确保元素存在时再赋值 --> :ref="(el) => { if (el) refs.value[item.name] = el }" > <MenuButton :class="[ item.current ? 'bg-gray-100' : 'hover:bg-gray-100', 'group flex h-10 w-full items-center justify-center rounded-md p-2.5 text-standard font-semibold leading-6 text-gray-700', ]" @click="getParentPosition($event, item.name)" > <span class="-mx-2.5 flex w-10 flex-none items-center justify-center" > <component :is="item.icon" class="h-5 w-5 shrink-0 text-gray-400" aria-hidden="true" /> </span> </MenuButton> <!-- 省略其他代码 --> </Menu> </li> </template> <script setup> import { ref } from 'vue' // 改为响应式对象存储字符串键对应的元素 const refs = ref({}) function getParentPosition(e, k) { const itemRef = refs.value[k] if (itemRef) { // 获取元素位置信息 const position = itemRef.getBoundingClientRect() console.log(position) } } </script>
内容的提问来源于stack exchange,提问作者ToxifiedM
相关产品推荐
相关产品推荐

