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

如何在Nuxtjs3中正确使用Suspense实现轮播骨架屏

Nuxt3中Suspense结合vue3-carousel失效问题解决

问题根源

你当前的实现有两个核心问题:

  • 组件本身通过<script setup>的顶层await已经成为异步组件,Nuxt会自动处理其加载状态,但你在模板内嵌套的<Suspense>与之产生冲突
  • vue3-carousel的<Carousel>组件要求直接子元素是<Slide>,而你用<Suspense>包裹了所有<Slide>,导致Carousel无法正确识别并渲染Slide元素,即便数据加载完成也会显示空白

修复方案

方案1:移除模板内Suspense,通过v-if控制骨架屏

直接利用组件的异步状态,通过preferredTours的存在与否来切换骨架屏和真实内容:

<template>
  <div class="ttc-py-1 sm:ttc-py-5">
    <div class="ttc-container ttc-mx-auto">
      <SectionHeader :heading="`Activities near you`" />
      <client-only>
        <Carousel
          :items-to-show="3.5"
          :wrap-around="true"
          :breakpoints="breakpoints"
        >
          <!-- 真实内容 -->
          <Slide
            v-for="(item, index) in preferredTours"
            :key="item.uniqueId" <!-- 建议用唯一ID而非index作为key -->
          >
            <CardsActivityCard
              :city="item.city"
              :country="item.country"
              :image="`${item.image}`"
              :packagename="item.package_name"
              :price="item.price"
              :uniqueid="item.uniqueId"
              :slug="item.slug"
            />
          </Slide>

          <!-- 骨架屏:仅在数据未加载时显示 -->
          <Slide v-for="i in 5" :key="`skeleton-${i}`" v-if="!preferredTours">
            <div>loading ...... or Skeltons</div>
          </Slide>
        </Carousel>
      </client-only>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
const { apiUrl } = useRuntimeConfig();
import { Carousel, Slide, Pagination, Navigation } from "vue3-carousel";
import "vue3-carousel/dist/carousel.css";

const [{ data: FavDests }, { data: preferredTours }] = await Promise.all([
  useFetch(`${apiUrl}/dests/getpopulardests?country=${userCountry}`, 
   { key: "cQsyfYspDP2zM1xMe0YY9Gfv4CgQByAd" }
  ),
  useFetch(
    `${apiUrl}/tours/getpopulartours?country=${userCountry}&usercurrency=${userCurrency}`,
    { key: "Z1rvmHPxAOLvt68WZZq2BUNsHQ8I213Y" }
  ),
]);
</script>

方案2:拆分异步子组件,用Suspense包裹

把轮播的内容部分拆成独立的异步组件,让子组件处理数据请求,父组件用<Suspense>控制加载状态:

子组件:TourCarouselItems.vue

<template>
  <Slide
    v-for="(item, index) in preferredTours"
    :key="item.uniqueId"
  >
    <CardsActivityCard
      :city="item.city"
      :country="item.country"
      :image="`${item.image}`"
      :packagename="item.package_name"
      :price="item.price"
      :uniqueid="item.uniqueId"
      :slug="item.slug"
    />
  </Slide>
</template>

<script setup>
const { apiUrl } = useRuntimeConfig();
// 接收父组件传递的参数
const props = defineProps(['userCountry', 'userCurrency']);

const { data: preferredTours } = await useFetch(
  `${apiUrl}/tours/getpopulartours?country=${props.userCountry}&usercurrency=${props.userCurrency}`,
  { key: "Z1rvmHPxAOLvt68WZZq2BUNsHQ8I213Y" }
);
</script>

父组件修改:

<template>
  <div class="ttc-py-1 sm:ttc-py-5">
    <div class="ttc-container ttc-mx-auto">
      <SectionHeader :heading="`Activities near you`" />
      <client-only>
        <Carousel
          :items-to-show="3.5"
          :wrap-around="true"
          :breakpoints="breakpoints"
        >
          <Suspense>
            <TourCarouselItems 
              :userCountry="userCountry" 
              :userCurrency="userCurrency" 
            />
            <template #fallback>
              <Slide v-for="i in 5" :key="`skeleton-${i}`">
                <div>loading ...... or Skeltons</div>
              </Slide>
            </template>
          </Suspense>
        </Carousel>
      </client-only>
    </div>
  </div>
</template>

<script setup>
import { ref } from "vue";
const { apiUrl } = useRuntimeConfig();
import { Carousel, Slide, Pagination, Navigation } from "vue3-carousel";
import "vue3-carousel/dist/carousel.css";
// 导入子组件
import TourCarouselItems from "./TourCarouselItems.vue";

// 处理FavDests请求
const { data: FavDests } = await useFetch(`${apiUrl}/dests/getpopulardests?country=${userCountry}`, 
 { key: "cQsyfYspDP2zM1xMe0YY9Gfv4CgQByAd" }
);
</script>

额外优化点

  • 不要用index作为v-for的key,改用数据中唯一标识(比如item.uniqueId),避免列表更新时出现渲染异常
  • 骨架屏可以替换为更接近真实卡片结构的骨架组件,提升用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:18:18