You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中v-for结合组件触发大量重复渲染的性能问题问询

为什么v-for渲染的组件列表在hover时会全部触发重渲染?

问题现象

当用v-for渲染TestComponent列表,给组件绑定原生@mouseenter/@mouseleave事件修改父组件的hoveredItem时,所有列表项都会触发render和patch流程。但出现以下情况时问题消失:

  • 移除<div v-if="hoveredItem">hovered</div>
  • 用普通div替代TestComponent
  • 手动写多个组件而非用v-for循环
  • 用子组件$emit自定义事件替代原生事件

根本原因

这是因为原生事件绑定在组件标签上时,Vue会把内联事件处理函数(比如@mouseenter="hoveredItem = item")作为新的函数引用传递给组件。

当父组件因为hoveredItem变化触发重渲染时,会为每个v-for循环的组件生成全新的事件处理函数引用。Vue的虚拟DOM diff算法会判定这些组件的事件绑定属性发生了变化,因此会触发每个组件的重新渲染,甚至执行patch更新事件监听。

而移除v-if="hoveredItem"后,hoveredItem的变化不会触发父组件重渲染——父组件不重新render,就不会生成新的内联函数,子组件的事件绑定引用不变,自然不会触发重渲染。

用普通div替代组件时,原生事件直接绑定在DOM元素上,Vue对DOM元素的事件处理逻辑和组件不同,只要元素属性没变化,就不会触发重渲染。

用子组件自定义事件时,父组件传递的是固定的方法引用(而非每次重渲染生成的新内联函数),子组件的属性不会被判定为变化,因此不会重渲染。

解决方案

方案1:将内联事件改为父组件方法

避免每次重渲染生成新的函数引用:

<template>
    <template v-for="item in list" :key="item.id">
        <TestComponent @mouseenter="handleItemHover(item)" @mouseleave="handleItemLeave" />
    </template>
    <div v-if="hoveredItem">hovered</div>
</template>

<script lang="ts">
import TestComponent from 'TestComponent.vue';
import { Options, Vue } from 'vue-class-component';
interface IItem {id:number, message:string};
@Options({
    props:{},
    components:{ TestComponent, }
})
export default class TestView extends Vue {
    public list:IItem[] = [];
    public hoveredItem:IItem|null = null;
    public mounted():void {
        for (let i = 0; i < 3; i++) {
            this.list.push({ id:i, message:"Message "+(i+1), });
        }
    }
    handleItemHover(item: IItem) {
        this.hoveredItem = item;
    }
    handleItemLeave() {
        this.hoveredItem = null;
    }
}
</script>

方案2:子组件封装原生事件后emit自定义事件

让子组件内部监听原生事件,再向父组件传递事件:
修改TestComponent:

<template>
    <div @mouseenter="$emit('item-hover')" @mouseleave="$emit('item-leave')">item</div>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
@Options({
    emits: ['item-hover', 'item-leave']
})
export default class TestComponent extends Vue {}
</script>

父组件使用:

<template>
    <template v-for="item in list" :key="item.id">
        <TestComponent @item-hover="hoveredItem = item" @item-leave="hoveredItem = null" />
    </template>
    <div v-if="hoveredItem">hovered</div>
</template>

内容的提问来源于stack exchange,提问作者Durss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 03:05:19