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

Vue图片加载时Loading Spinner异常显示问题求助

问题描述

我希望在图片加载时显示Loading Spinner,但不知如何正确实现。目前控制台能正常打印isLoading的true和false值,但Spinner仍不显示;若移除isLoading=false的设置,Spinner会一直显示。

Vue组件代码

<template>
    <div class="KingOfMountain">
        <Spinner v-if="isLoading"/> //// ERROR
        <div v-else class="container">
            <div v-if="!isEndGameKing" class="choices">
                <p id="score">{{ currentCountKing }}/{{ ALL_FILMS.length - 1 }}
                <p/>
                <div class="photos">
                    <div class="first__film">
                        <img :src="firstFilm.Poster" :alt="firstFilm.title" @click="chooseLeftFilm">
                        <p id="title--film">{{ firstFilm.title }}</p>
                    </div>
                    <div class="second__film">
                        <img :src="secondFilm.Poster" :alt="secondFilm.title" @click="chooseRightFilm">
                        <p id="title--film">{{ secondFilm.title }}</p>
                    </div>
                </div>
            </div>
            <div v-else class="winner">
                <p id="winner--title">Победитель</p>
                <img :src="firstFilm.Poster" :alt="firstFilm.title">
            </div>
        </div>
    </div>
</template>

<script>
import game from "@/mixins/game";
import Spinner from "@/components/Spinner/Spinner"; //all good in css . it works

export default {
    name: "KingOfMountain",
    data() {
        return{
            isLoading:false
        }
    },
    components: {Spinner},
    methods: {
        chooseLeftFilm() {
            this.isLoading=true
            this.redirectToResultKing() // it is method in mixins (all Good, it works)
            this.isLoading=false
        },
        chooseRightFilm() {
            this.isLoading=true
            this.firstFilm = this.secondFilm;
            this.redirectToResultKing() // it is method in mixins (all Good, it works)
            this.isLoading=false
        }
    },
}
</script>

若修改为以下代码,Spinner会一直显示:

chooseLeftFilm() {
    this.isLoading=true
    this.redirectToResultKing() // it is method in mixins (all Good, it works)
},

混入代码

export default {
methods: {
    updateFilm() {
        // Here i random take 2 images from Vuex and then them delete and etc...
        this.currentCountKing++;
        this.allFilmsKingCopy = this.allFilmsKingCopy.filter(val => val !== this.secondFilm)
        this.secondFilm = this.allFilmsKingCopy[Math.floor(Math.random() * this.allFilmsKingCopy.length)]
    },
    redirectToResultKing() {
        if (this.currentCountKing === this.ALL_FILMS.length - 1) {
            this.isEndGameKing = true
        } else {
            this.updateFilm()
        }
    }
},
computed: {
    ...mapGetters(['ALL_FILMS']),
},

Vuex代码

export default {
    state: {
        films: [],
    },
    actions: {
        async getFilms({commit}) {
            const data = await fetch(URL);
            const dataResponse = await data.json();
            const films=dataResponse.data
            commit("setData", films)
        },
    },
    mutations: {
        setData(state, films) {
            state.films = films
        },
    },
    getters: {
        ALL_FILMS(state) {
            return state.films
        },
    }
}

解决方案

核心问题分析

  1. Vue响应式更新异步特性:设置isLoading=true后立刻执行isLoading=false,Vue的DOM更新是异步队列处理的,Spinner还没来得及渲染就被隐藏,所以看不到效果。
  2. 错误的时机判断:当前代码在数据更新后立刻隐藏Spinner,但实际需要等待新图片加载完成后再隐藏,而非数据更新完成就隐藏。

具体修改步骤

1. 监听图片加载完成事件

在模板的img标签添加@load事件,触发图片加载完成后的逻辑:

<div class="first__film">
    <img :src="firstFilm.Poster" :alt="firstFilm.title" @click="chooseLeftFilm" @load="onImageLoad">
    <p id="title--film">{{ firstFilm.title }}</p>
</div>
<div class="second__film">
    <img :src="secondFilm.Poster" :alt="secondFilm.title" @click="chooseRightFilm" @load="onImageLoad">
    <p id="title--film">{{ secondFilm.title }}</p>
</div>

2. 调整组件方法逻辑

移除方法中直接设置isLoading=false的代码,改为在图片加载完成后隐藏:

methods: {
    chooseLeftFilm() {
        this.isLoading = true
        this.redirectToResultKing()
    },
    chooseRightFilm() {
        this.isLoading = true
        this.firstFilm = this.secondFilm;
        this.redirectToResultKing()
    },
    onImageLoad() {
        // 简单场景下可加短暂延迟,确保DOM更新完成
        setTimeout(() => {
            this.isLoading = false
        }, 100)
    }
}

3. 多图片加载的严谨判断(可选)

如果需要确保两张图片都加载完成再隐藏Spinner,可添加状态变量跟踪:

data() {
    return {
        isLoading: false,
        leftImageLoaded: false,
        rightImageLoaded: false
    }
},
methods: {
    chooseLeftFilm() {
        this.isLoading = true
        this.leftImageLoaded = false
        this.rightImageLoaded = false
        this.redirectToResultKing()
    },
    chooseRightFilm() {
        this.isLoading = true
        this.leftImageLoaded = false
        this.rightImageLoaded = false
        this.firstFilm = this.secondFilm;
        this.redirectToResultKing()
    },
    onLeftImageLoad() {
        this.leftImageLoaded = true
        this.checkAllImagesLoaded()
    },
    onRightImageLoad() {
        this.rightImageLoaded = true
        this.checkAllImagesLoaded()
    },
    checkAllImagesLoaded() {
        if (this.leftImageLoaded && this.rightImageLoaded) {
            this.isLoading = false
        }
    }
}

对应模板修改:

<img :src="firstFilm.Poster" :alt="firstFilm.title" @click="chooseLeftFilm" @load="onLeftImageLoad">
<img :src="secondFilm.Poster" :alt="secondFilm.title" @click="chooseRightFilm" @load="onRightImageLoad">

额外提示

  • 若updateFilm中有异步操作,需确保异步操作完成后再等待图片加载。
  • 建议给Spinner添加全屏覆盖样式,避免用户重复点击,例如:
.spinner {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 999;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:02:10