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

Vue组件首次加载不显示,热重载后有时显示问题求助

问题:SpecChampionData组件首次加载不显示,仅热重载后偶然出现

我有一个SpecPlayer.vue文件,其中使用了SpecChampionData组件,该组件首次加载时完全不显示,仅在热重载后(也不是每次都能成功)才会出现。我是Vue新手,日常不用TS或JS,希望能搞懂问题本质并解决。

SpecPlayer.vue代码

<script setup lang="ts">
import * as art from '@/getArt'
import SpecChampionData from './SpecChampionData.vue'
import { Player } from '@/types/ddata'

defineProps<{
    player: Player | undefined
}>()
</script>
<template>
    <div class="w-full p-4 rounded-md bg-semi">
        <div class="flex flex-row gap-2 flex-nowrap">
            <div class="relative h-20 border-2 aspect-square avatar border-primary">
                <div>
                    <div v-if="player?.isDead"
                        class="absolute inset-0 z-20 flex items-center justify-center aspect-square h-full text-[#ff0000]">
                        <h1 class="text-3xl font-bold">{{ Math.ceil(player.respawnTimer || 0) }}</h1>
                    </div>
                    <img v-if="player?.championName" class="avatar" :class="{
                        'grayscale brightness-50': player?.isDead
                    }" :src="art.getChampIcon(player?.championName)">
                    <img v-else src="/BigFist.png" class="grayscale">
                    <div
                        class="absolute bottom-0 w-7 left-[50%] z-10 translate-x-[-50%] translate-y-[50%] bg-semi border-2 border-primary rounded-full aspect-square flex justify-center items-center">
                        <h1 class="text-sm font-medium text-center text-white">{{ player?.level || 0 }}</h1>
                    </div>
                </div>
            </div>
            <div class="flex flex-col">
                <h1 class="text-xl font-semibold text-primary">{{ player?.summonerName || 'Jugador' }}</h1>
                <h1 class="text-white">{{ player?.championName }} <h1 v-if="player?.skinName"> ({{ player?.skinName }})
                    </h1>
                </h1>
            </div>
        </div>
        <!-- Here is the component -->
        <SpecChampionData :player="player"></SpecChampionData>
    </div>
</template>

SpecChampionData.vue代码

<template>
    <h1>{{ champData?.lore }}</h1>
</template>
<script setup lang="ts">
import axios from 'axios'
import { Champion, ChampionResponse, Player } from '@/types/ddata'
import { Ref, ref } from 'vue'

const props = defineProps<{
    player: Player | undefined
}>()
const champData: Ref<Champion | undefined> = ref()

const champName = props.player?.championName

axios.get(`https://ddragon.leagueoflegends.com/cdn/14.5.1/data/es_AR/champion/${champName}.json`)
    .then(res => {
        const resData: ChampionResponse = res.data
        if (champName) {
            champData.value = resData.data[champName]
        }
    })
</script>

问题本质分析

1. 首次加载时player可能未初始化

组件初始化时直接读取props.player?.championName,但如果父组件的player是异步获取(比如从API或其他异步逻辑加载),首次渲染时player还是undefined,此时champName为undefined,axios请求的URL会变成.../champion/undefined.json,请求失败后无法获取英雄数据,组件自然显示空内容。

热重载时,父组件的player数据可能已经加载完成,此时champName有有效值,请求能成功,所以组件能显示内容。

2. 生命周期钩子没解决的原因

你之前尝试的onBeforeMount/onMounted只在组件初始化时执行一次,如果执行时player仍为undefined,后续player更新时不会重新触发请求,问题依然存在。

解决方案

1. 监听player变化,自动触发请求

修改SpecChampionData.vue,使用watch监听player.championName的变化,每当英雄名称更新或首次初始化时,自动发起请求:

<template>
    <h1>{{ champData?.lore || '暂无英雄背景故事' }}</h1>
</template>
<script setup lang="ts">
import axios from 'axios'
import { Champion, ChampionResponse, Player } from '@/types/ddata'
import { Ref, ref, watch } from 'vue'

const props = defineProps<{
    player: Player | undefined
}>()
const champData: Ref<Champion | undefined> = ref()

// 封装请求逻辑,方便复用
const fetchChampionData = () => {
    const champName = props.player?.championName
    if (!champName) {
        champData.value = undefined
        return
    }
    // 发起请求获取英雄数据
    axios.get(`https://ddragon.leagueoflegends.com/cdn/14.5.1/data/es_AR/champion/${champName}.json`)
        .then(res => {
            const resData: ChampionResponse = res.data
            champData.value = resData.data[champName]
        })
        .catch(err => {
            console.error('获取英雄数据失败:', err) // 打印错误方便排查
        })
}

// 监听英雄名称变化,自动执行请求
watch(
    () => props.player?.championName, // 只监听英雄名称字段
    () => fetchChampionData(),
    { immediate: true } // 组件初始化时立即执行一次
)
</script>

2. 增加加载状态(可选优化)

如果请求需要一定时间,可添加加载提示提升用户体验:

<template>
    <div v-if="isLoading" class="text-white">加载中...</div>
    <h1 v-else>{{ champData?.lore || '暂无英雄背景故事' }}</h1>
</template>
<script setup lang="ts">
// 保留上面的代码,新增加载状态变量
const isLoading = ref(false)

const fetchChampionData = () => {
    const champName = props.player?.championName
    if (!champName) {
        champData.value = undefined
        isLoading.value = false
        return
    }
    isLoading.value = true // 开始加载
    axios.get(...)
        .then(res => {
            // 处理数据逻辑
            isLoading.value = false // 加载完成
        })
        .catch(err => {
            console.error(err)
            isLoading.value = false // 加载失败也关闭加载状态
        })
}
</script>

3. 父组件条件渲染(可选优化)

在SpecPlayer.vue中,只有当player存在时再渲染SpecChampionData,避免无效请求:

<!-- 替换原有的SpecChampionData标签 -->
<SpecChampionData v-if="player" :player="player"></SpecChampionData>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:06:03