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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 00:17:28