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

Vue监听器设置枚举变量时v-if更新异常问题

Vue中v-if对特定枚举值判定异常问题

问题现象

在Vue 3 + TypeScript的单文件组件中,通过监听器监听game.notation更新当前回合玩家whoseTurn(类型为枚举Color)。当回合切换到玩家A时,模板中v-if="whoseTurn"判定为假,页面显示"No turn",但控制台日志明确显示whoseTurn已被正确设置为Color.A,且其他玩家(B/C/D)的回合都能正常显示。

组件代码

<script lang="ts">
// imports ...

export default defineComponent({
    data() {
        return {
            store,
            api: new Api(Constants.baseUrl),
            game: null as (Game | null),
            playerIcons: Constants.playerIcons,
            gameId: this.$route.params.id as string,
            poller: null as number | null,
            whoseTurn: null as Color | null,
        };
    },
    methods: {
        async pollGame(): Promise<void> {
            try {
                this.game = await this.api.readGame(this.gameId);
                // snip
            } catch (error) {
                alert(error);
            }
        },
        colorToNotation(color: Color): string | null {
            if (color === Color.A) {
                return "A";
            } else if (color === Color.B) {
                return "B";
            } else if (color === Color.C) {
                return "C";
            } else if (color === Color.D) {
                return "D";
            }

            return null;
        },
        notationToColor(notation: string): Color | null {
            if (notation === 'a') {
                return Color.A;
            } else if (notation === 'b') {
                return Color.B;
            } else if (notation === 'c') {
                return Color.C;
            } else if (notation === 'd') {
                return Color.D;
            }

            return null;
        }
    },
    async mounted() {
        this.pollGame();
        this.poller = setInterval(this.pollGame, Constants.pollingInterval);
    },
    unmounted() {
        if (this.poller) {
            clearInterval(this.poller!);
        }
    },
    watch: {
        'game.notation'(newValue) {
            // snip

            const playerNotation: string = newValue[newValue.length - 1];
            let previousTurn: Color | null = this.whoseTurn;
            this.whoseTurn = this.notationToColor(playerNotation);
            console.log(`Set turn from ${this.colorToNotation(previousTurn!)} to ${this.colorToNotation(this.whoseTurn!)}`);
        }
    }

    // snip
});
</script>

<template>
    <!-- snip -->
    <div v-if="game">
        <div id="whose-turn" v-if="whoseTurn">
            Turn:
            <img class="player-icon" :src="playerIcons.get(whoseTurn)" />
            {{ game.getPlayer(whoseTurn)!.name }}
        </div>
        <div v-else>
            No turn
        </div>

        <!-- snip -->
    </div>
    <!-- snip -->
</template>

控制台日志

Set turn from null to A
Set turn from A to B
Set turn from B to C
Set turn from C to D
Set turn from D to A
Set turn from A to B
Set turn from B to C
Set turn from C to D
Set turn from D to A
...

页面显示

No turn
Turn: B
Turn: C
Turn: D
No turn
Turn: B
Turn: C
Turn: D
...

问题原因

核心问题出在TypeScript数值枚举的默认取值:如果你的Color枚举是默认定义的(未手动指定值),那么Color.A的数值是0,而Vue模板中的v-if会将0视为布尔假值(JavaScript中0、''、null、undefined、NaN都是假值)。其他枚举值B/C/D默认是1、2、3,会被判定为真值,所以能正常显示。

解决方案

方案1:修改枚举起始值

将Color枚举的起始值设为1,避免0的出现:

enum Color {
  A = 1,
  B, // 自动为2
  C, // 自动为3
  D  // 自动为4
}

方案2:修改v-if判定条件

如果无法修改枚举定义,直接在模板中明确检查whoseTurn是否不为null,而不是依赖自动类型转换:

<div id="whose-turn" v-if="whoseTurn !== null">

方案3:使用字符串枚举

改用字符串枚举,确保所有枚举值都是非空字符串,不会被判定为假值:

enum Color {
  A = 'A',
  B = 'B',
  C = 'C',
  D = 'D'
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:51:32