Next.js 13路由切换时如何实现加载页面?
Next.js 13 路由切换加载状态不生效问题解决方案
问题根源
你当前的代码把Suspense包裹了Header、页面内容和NavigationBar,但Header和导航栏是布局中固定的静态元素,路由切换时它们不会重新渲染,导致Suspense无法捕获页面内容的加载状态。另外,若页面组件没有异步操作(比如服务器组件的fetch),Suspense也不会触发。
解决方案1:调整Suspense位置
将Suspense仅包裹页面内容{children},让Header和导航栏始终显示,只有页面切换时触发加载:
export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <Head> <link rel="icon" href="/favicon.ico" /> </Head> <body> <Script src="https://telegram.org/js/telegram-web-app.js" /> <Providers> <Header /> {/* 仅对页面内容使用Suspense */} <Suspense fallback={<LoadingPage />}> {children} </Suspense> <NavigationBar /> </Providers> <Toaster closeButton={false} position="bottom-center" toastOptions={{ duration: 3000, }} /> </body> </html> ); }
解决方案2:监听路由切换事件(客户端组件)
如果页面是静态的无异步操作,或者需要更精准的加载状态控制,可通过useRouter监听路由切换事件:
- 创建客户端组件
LoadingWrapper.tsx:
'use client'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; export default function LoadingWrapper({ children }: { children: React.ReactNode }) { const router = useRouter(); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const startLoading = () => setIsLoading(true); const endLoading = () => setIsLoading(false); // 绑定路由切换事件 router.events.on('routeChangeStart', startLoading); router.events.on('routeChangeComplete', endLoading); router.events.on('routeChangeError', endLoading); // 组件卸载时解绑事件 return () => { router.events.off('routeChangeStart', startLoading); router.events.off('routeChangeComplete', endLoading); router.events.off('routeChangeError', endLoading); }; }, [router]); return isLoading ? <LoadingPage /> : <>{children}</>; }
- 在RootLayout中使用该Wrapper:
<Providers> <Header /> <LoadingWrapper> {children} </LoadingWrapper> <NavigationBar /> </Providers>
注意事项
- 若页面是服务器组件,需包含异步操作(如
fetch)才能触发Suspense的fallback; useRouter的事件监听仅支持客户端组件,必须添加'use client'指令;- 确保
LoadingPage组件可以正常在服务器或客户端渲染,避免 hydration 错误。
内容的提问来源于stack exchange,提问作者Oguzhan
相关产品推荐
相关产品推荐

