Vue3中如何为计算属性返回的div数组重新绑定事件监听器?
问题原因及解决方案
核心问题
你遇到的问题源于三个关键错误:
- watch监听对象错误:你监听的是
filteredArticles.value(普通数组)而非响应式的filteredArticles计算属性,导致过滤条件变化时articleRefs未被正确重置,新渲染的DOM元素会追加到旧的ref数组中,索引完全混乱。 - 依赖v-for的index匹配DOM:数组被过滤/重新排序后,v-for的index会重新计算,但ref数组的索引和实际渲染的DOM元素无法一一对应,hover时会取到错误的DOM元素。
- 不必要的DOM操作:用ref手动操作DOM不符合Vue的声明式设计思路,反而容易引发索引不匹配问题。
推荐解决方案:声明式类绑定(无需操作DOM)
利用Vue的响应式系统,完全避免索引和DOM匹配问题:
模板代码
<article v-for="article in filteredArticles" :key="article.id" @mouseenter="hoveredArticleId = article.id" @mouseleave="hoveredArticleId = null" :class="{ animate: hoveredArticleId === article.id }" > <h2 :class="{ glitch: hoveredArticleId === article.id }"> <!-- 标题内容 --> </h2> <!-- 其他内容 --> </article>
脚本代码
const articleList = ref([] as Article[]) const activeTag = ref('') // 跟踪当前hover的文章ID const hoveredArticleId = ref<number | null>(null) const filteredArticles = computed(() => { return activeTag.value !== '' ? articleList.value.filter(a => a.tags.some(t => t.Tag_id.title === activeTag.value)) : articleList.value })
修复原有代码的方案(若坚持DOM操作)
如果需要保留原有的DOM操作逻辑,需做以下修改:
- 修正watch监听对象,确保响应式触发
- 通过事件对象直接获取当前元素,不再依赖index
模板代码
<article v-for="article in filteredArticles" :key="article.id" @mouseenter="handleClass($event, 'add')" @mouseleave="handleClass($event, 'remove')" > <!-- Content --> </article>
脚本代码
const articleList = ref([] as Article[]) const activeTag = ref('') const filteredArticles = computed(() => { return activeTag.value !== '' ? articleList.value.filter(a => a.tags.some(t => t.Tag_id.title === activeTag.value)) : articleList.value }) // 修正watch监听,监听响应式计算属性 watch(filteredArticles, () => { // 不再依赖ref数组,此处可省略清空逻辑 }, { immediate: true }) const handleClass = (event: MouseEvent, action: 'add'|'remove') => { // 通过事件对象获取当前触发hover的元素 const hoveredArticle = event.currentTarget as HTMLDivElement hoveredArticle.classList[action]('animate') hoveredArticle.querySelector('h2')?.classList[action]('glitch') }
内容的提问来源于stack exchange,提问作者Jaeger
相关产品推荐
相关产品推荐

