Next.js中使用getServerSideProps切换路由时显示加载状态的问题
解决Next.js中getServerSideProps页面通过Link跳转时的加载状态提示问题
我完全懂你的痛点——用Link跳转带getServerSideProps的页面时,客户端需要异步拉取数据,中间那几秒的“无响应”空白期真的很影响体验。下面给你两种实用方案,既能保留直连页面的SSR能力,又能给用户清晰的加载反馈:
方案一:全局路由加载指示器(推荐)
这种方法会监听所有路由跳转事件,在数据加载期间显示全局加载动画,适合给整个应用统一的体验。
修改你的pages/_app.js文件:
import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Layout from '../components/Layout'; // 替换成你自己的Layout组件 function MyApp({ Component, pageProps }) { 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 && ( <div style={{ position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(255, 255, 255, 0.9)', zIndex: 9999, fontSize: '1.5rem' }}> 加载中... {/* 这里可以替换成你自己的Spinner组件 */} </div> )} <Layout> <Component {...pageProps} /> </Layout> </> ); } export default MyApp;
为什么这个方案有效?
- 直接访问
/post/:id时,SSR已经在服务端完成了数据获取和页面渲染,路由事件不会触发加载动画,完全保留原有的SSR体验。 - 通过
Link跳转时,routeChangeStart会立即触发,显示加载动画;等fetchNextData请求完成、路由切换成功后,routeChangeComplete会关闭加载动画,用户能清楚感知到页面正在加载。
方案二:局部加载提示(针对单个页面跳转)
如果你只想在从/posts跳转到/post/:id时显示加载提示,可以在/posts页面中处理:
import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/router'; function Posts({ posts }) { const [isLoadingPost, setIsLoadingPost] = useState(false); const router = useRouter(); useEffect(() => { const stopLoading = () => setIsLoadingPost(false); // 路由完成或出错时停止加载提示 router.events.on('routeChangeComplete', stopLoading); router.events.on('routeChangeError', stopLoading); return () => { router.events.off('routeChangeComplete', stopLoading); router.events.off('routeChangeError', stopLoading); }; }, [router]); return ( <div className="posts-list"> {/* 局部加载提示 */} {isLoadingPost && <div className="loading-tip">正在加载文章...</div>} {posts.map(post => ( <Link key={post.id} as={`/post/${post.id}`} href={`/post?id=${post.id}`} onClick={() => setIsLoadingPost(true)} passHref > <a className="post-link">{post.name}</a> </Link> ))} </div> ); } // 你的getServerSideProps或getStaticProps逻辑 export async function getStaticProps() { const res = await fetch(`${process.env.API_URL}/api/posts`); const posts = await res.json(); return { props: { posts } }; } export default Posts;
注意事项
- 两种方案都不会影响直连页面的SSR效果,因为直连时路由直接完成,不会触发
routeChangeStart事件。 - 你可以把示例中的“加载中...”文本替换成更美观的Spinner组件(比如自定义CSS动画或第三方组件库)。
内容的提问来源于stack exchange,提问作者HMR
相关产品推荐
相关产品推荐

