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

Vue3 Composition API动态CSS类引发递归更新超限警告求助

问题原因

你碰到的「Maximum recursive updates exceeded」警告,根源是setImgIcon函数里修改了全局的reduceSizeIcon响应式变量,而这个变量又绑定在模板的动态类上。每一次渲染<img>时,调用setImgIcon会更新reduceSizeIcon的值,触发组件重新渲染;重新渲染又会再次调用setImgIcon,形成无限循环,最终触发Vue的递归更新保护机制。

解决方法

核心思路是让每个列表项独立管理自己的图标和类名状态,不要用全局响应式变量共享状态,避免在模板调用的函数里修改响应式变量。

方案一:让函数返回包含图标地址和类名的对象

修改setImgIcon为返回包含src和className的对象,每个列表项获取自己的独立状态:

<script setup lang="ts">
import { ref } from "vue";
import { ISugerenciaGlobalStar } from "@/types/IMapas";
// 图标导入部分保持不变
import aviaFrance from '@/assets/AVIA FRANCE.svg'
import petromiralles from '@/assets/PETROMIRALLES.svg'
import andamur from '@/assets/ANDAMUR.svg'
import cepsa from '@/assets/CEPSA.svg'
import esso from '@/assets/ESSO.svg'
import galp from '@/assets/GALP.svg'
import nieves from '@/assets/NIEVES.svg'
import q8 from '@/assets/Q8.svg'
import repsol from '@/assets/REPSOL.svg'
import shell from '@/assets/SHELL.svg'
import texaco from '@/assets/TEXACO.svg'
import valcarce from '@/assets/VALCARCE.svg'
import eni from '@/assets/ENI.svg'
import mtw from '@/assets/MTW.svg'
import tpetromiralles from '@/assets/T-PETROMIRALLES.svg'
import globalStar from '@/assets/web/logo-globalstar.png'

const props = defineProps<{
    sugerenciasGlobarStar: ISugerenciaGlobalStar[];
}>();

// 修改函数,返回包含src和类名的对象
function getIconInfo(marca: string): { src: string, className: string } {
    switch (marca) {
        case 'AVIA':
        case 'AVIA FRANCE':
            return { src: aviaFrance, className: "" };
        case 'PETROMIRALLES':
            return { src: petromiralles, className: "" };
        case 'T-PETROMIRALLES':
            return { src: tpetromiralles, className: "" };
        case 'ANDAMUR':
            return { src: andamur, className: "" };
        case 'CEPSA':
            return { src: cepsa, className: "" };
        case 'ESSO':
            return { src: esso, className: "" };
        case 'GALP':
            return { src: galp, className: "" };
        case 'NIEVES':
            return { src: nieves, className: "" };
        case 'Q8':
            return { src: q8, className: "" };
        case 'REPSOL':
            return { src: repsol, className: "" };
        case 'SHELL':
            return { src: shell, className: "" };
        case 'TEXACO':
            return { src: texaco, className: "" };
        case 'VALCARCE':
            return { src: valcarce, className: "" };
        case 'ENI':
            return { src: eni, className: "" };
        case 'MTW':
            return { src: mtw, className: "" };
        default:
            return { src: globalStar, className: "mapIconSugerencias" };
    }
}
</script>

模板部分可以用v-bind直接展开对象,更简洁:

<template>
    <v-card v-for="(objeto, key) in sugerenciasGlobarStar"
            elevation="6" class="mt-2 pa-2" :key="key">
        <v-row justify="center" align="center" class="mr-0 ml-0">
            <v-col cols="2" align="center">
                <!-- 直接用v-bind展开返回的对象 -->
                <img v-bind="getIconInfo(objeto.marca)" />
            </v-col>
            <!-- 其余模板内容保持不变 -->
            <v-col cols="10">
                <v-card-item class="ma-0 pa-0 text-caption">
                    <template v-slot:prepend>
                        {{ objeto.marca }}
                    </template>
                    <template v-slot:append>
                        <v-icon v-if="objeto.favorito" color="amber-accent-4"
                                class="mr-2 pb-2">mdi-star</v-icon>
                        <span v-if="objeto.precio != '-'">
                            {{ objeto.precio + " €" }}
                        </span>
                    </template>
                </v-card-item>
                <v-divider></v-divider>
                <v-card-item class="ma-0 pa-0 text-caption">
                    <template v-slot:prepend>
                        <span>{{ objeto.provincia }}</span>
                    </template>
                    <template v-slot:append>
                        <span>{{ objeto.poblacion }}</span>
                    </template>
                </v-card-item>
            </v-col>
        </v-row>
    </v-card>
</template>

<style scoped>
.mapIconSugerencias {
    width: 100%;
}
</style>

方案二:拆分图标地址和类名的获取函数

如果不想修改函数返回结构,可以把获取图标地址和类名的逻辑拆成两个独立函数,避免在获取地址时修改全局变量:

<script setup lang="ts">
// ...导入部分不变

const props = defineProps<{
    sugerenciasGlobarStar: ISugerenciaGlobalStar[];
}>();

// 单独获取图标地址
function getIconSrc(marca: string): string {
    switch (marca) {
        case 'AVIA':
        case 'AVIA FRANCE': return aviaFrance;
        case 'PETROMIRALLES': return petromiralles;
        case 'T-PETROMIRALLES': return tpetromiralles;
        case 'ANDAMUR': return andamur;
        case 'CEPSA': return cepsa;
        case 'ESSO': return esso;
        case 'GALP': return galp;
        case 'NIEVES': return nieves;
        case 'Q8': return q8;
        case 'REPSOL': return repsol;
        case 'SHELL': return shell;
        case 'TEXACO': return texaco;
        case 'VALCARCE': return valcarce;
        case 'ENI': return eni;
        case 'MTW': return mtw;
        default: return globalStar;
    }
}

// 单独获取类名
function getIconClass(marca: string): string {
    // 定义不需要加类的品牌列表
    const normalBrands = ['AVIA', 'AVIA FRANCE', 'PETROMIRALLES', 'T-PETROMIRALLES', 'ANDAMUR', 'CEPSA', 'ESSO', 'GALP', 'NIEVES', 'Q8', 'REPSOL', 'SHELL', 'TEXACO', 'VALCARCE', 'ENI', 'MTW'];
    return normalBrands.includes(marca) ? "" : "mapIconSugerencias";
}
</script>

模板部分分别调用两个函数:

<img :src="getIconSrc(objeto.marca)" :class="getIconClass(objeto.marca)" />

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 00:57:20