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,再异步发起请求:
- 修改请求逻辑:给
useFetch添加lazy: true选项,避免阻塞组件挂载 - 用请求的
pending状态控制Spinner:去掉自定义的loading变量和setTimeout,直接依赖useFetch返回的状态 - 用计算属性处理数据:避免在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(无论目标页面是否开始渲染),可以通过路由守卫实现:
- 创建路由守卫插件
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); });
- 在
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
相关产品推荐
相关产品推荐

