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

Nuxt 3页面跳转时加载Spinner无法即时显示的解决方法

页面跳转时即时显示加载Spinner的解决方案

问题场景

我有一个Nuxt 3 + Vue 3应用,包含两个页面:

  • pages/index.vue 首页
  • pages/folder1/folder2/[dynamic_route].vue 动态路由页面

从首页跳转到动态路由页面时,加载Spinner无法立即显示:点击跳转后页面停留在首页,等待一段时间后才完成跳转,随后Spinner短暂显示,最后图表加载完成。期望点击跳转后Spinner能即时显示。

该动态路由页面配置了客户端渲染(ssr: false),且包含3个API调用。

核心原因

动态路由页面的await useFetch是在组件setup阶段同步执行的,这会阻塞组件挂载——只有当所有API请求完成后,组件才会被渲染,页面才会完成跳转。加上客户端渲染没有预渲染能力,等待时间被进一步拉长,导致Spinner无法及时显示。

解决方案

方案1:优化页面内的请求与加载状态

修改动态路由页面的代码,让组件先挂载渲染Spinner,再异步发起请求:

  1. 修改请求逻辑:给useFetch添加lazy: true选项,避免阻塞组件挂载
  2. 用请求的pending状态控制Spinner:去掉自定义的loading变量和setTimeout,直接依赖useFetch返回的状态
  3. 用计算属性处理数据:避免在setup阶段直接依赖未就绪的data.value

修改后的pages/folder1/folder2/[dynamic_route].vue代码:

<template>
  <div>
    <!-- 直接用pending状态控制Spinner显示 -->
    <div v-if="pending" class="loader">Loading...</div>

    <div v-else class="chartSection">
      <div v-if="!error" class="chart">
        <ApexchartsLineChart :top20-items="top20Items" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { createError } from "h3";
definePageMeta({ middleware: "auth" });

const baseURL = `/api/proxy/some_path`;
const route = useRoute();
const dynamicRoute = route.params.dynamic_route;

// 添加lazy: true,让请求不阻塞组件挂载
const {
  data,
  error,
  pending,
} = useFetch(`${baseURL}/another_path/${dynamicRoute}`, {
  lazy: true
});

// 监听错误状态,抛出错误
watch(error, () => {
  if (error.value || !data.value) {
    throw createError({
      message: "Error in data section",
      statusCode: error.value?.statusCode,
      statusMessage: error.value?.statusMessage,
      fatal: true,
    });
  }
});

// 用计算属性处理数据,确保data就绪后再处理
const top20Items = computed(() => {
  return data.value ? JSON.parse(JSON.stringify(data.value.slice(0, 20))) : [];
});
</script>

<style scoped lang="scss">
@import "@/assets/styles/loaders/loadingDotsSpinner.scss";
@import "@/assets/styles/pages/chartPage.scss";
</style>

方案2:添加全局路由级Spinner(更彻底的解决)

如果希望点击跳转时立即显示全局Spinner(无论目标页面是否开始渲染),可以通过路由守卫实现:

  1. 创建路由守卫插件plugins/router-loading.ts:
export default defineNuxtPlugin((nuxtApp) => {
  const router = useRouter();
  const pageLoading = ref(false);

  // 跳转开始时显示Spinner
  router.beforeEach(() => {
    pageLoading.value = true;
  });

  // 跳转完成后隐藏Spinner
  router.afterEach(() => {
    pageLoading.value = false;
  });

  // 路由出错时也隐藏Spinner
  router.onError(() => {
    pageLoading.value = false;
  });

  // 将loading状态提供给全局使用
  nuxtApp.provide('pageLoading', pageLoading);
});
  1. 在app.vue中添加全局Spinner:
<template>
  <div>
    <!-- 全局Spinner,覆盖整个页面 -->
    <div v-if="$pageLoading" class="global-loader">
      <div class="loader">Loading...</div>
    </div>
    <NuxtPage />
  </div>
</template>

<style>
.global-loader {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(255,255,255,0.9);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
}
</style>

效果说明

  • 方案1:页面跳转后会立即渲染动态路由页面的Spinner,同时后台发起API请求,请求完成后显示图表
  • 方案2:点击跳转按钮的瞬间就会显示全局Spinner,彻底解决首页停留的问题,跳转完成后隐藏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 17:17:31