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

Next.js 13 Pages目录下SSR页面如何添加默认加载状态?

Next.js 13 Pages目录下实现SSR加载状态UI

针对你用getServerSideProps时数据加载慢、直接打开页面路由事件不生效的问题,给你两种覆盖全场景的实现方案:

方案一:自定义Document + App组件,覆盖初始请求+客户端跳转

这个方案能同时处理直接打开/刷新页面和客户端路由跳转两种场景。

1. 自定义_document.js添加初始加载UI

_document.js是服务端渲染的最外层模板,会在getServerSideProps处理数据期间先返回给客户端,所以可以在这里加一个全局加载UI:

import Document, { Html, Head, Main, NextScript } from 'next/document';

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head />
        <Body>
          {/* 初始加载UI,服务端返回页面内容前先显示 */}
          <div id="initial-loader" style={{
            position: 'fixed',
            inset: 0,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#ffffff',
            zIndex: 9999,
          }}>
            <div>加载中,请稍候...</div>
          </div>
          <Main />
          <NextScript />
        </Body>
      </Html>
    );
  }
}

export default MyDocument;

2. 自定义_app.js处理路由状态与加载UI隐藏

在_app.js里监听路由事件,处理客户端跳转时的加载状态,同时在页面 hydration 完成后隐藏初始加载UI:

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  const [isRouteLoading, setIsRouteLoading] = useState(false);

  useEffect(() => {
    // 页面 hydration 完成后,隐藏初始加载UI
    const initialLoader = document.getElementById('initial-loader');
    if (initialLoader) initialLoader.style.display = 'none';

    // 监听客户端路由跳转事件
    const startLoading = () => setIsRouteLoading(true);
    const stopLoading = () => setIsRouteLoading(false);

    router.events.on('routeChangeStart', startLoading);
    router.events.on('routeChangeComplete', stopLoading);
    router.events.on('routeChangeError', stopLoading);

    // 组件卸载时移除监听
    return () => {
      router.events.off('routeChangeStart', startLoading);
      router.events.off('routeChangeComplete', stopLoading);
      router.events.off('routeChangeError', stopLoading);
    };
  }, [router]);

  return (
    <>
      {/* 客户端路由跳转时显示的加载UI */}
      {isRouteLoading && (
        <div style={{
          position: 'fixed',
          inset: 0,
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          backgroundColor: '#ffffff',
          zIndex: 9999,
        }}>
          <div>加载中,请稍候...</div>
        </div>
      )}
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

3. 页面组件兜底处理(可选)

虽然getServerSideProps出错概率低,但还是可以在页面组件里加个兜底,防止数据未加载时显示空白:

export default function TargetPage({ data }) {
  // 数据未获取到时显示加载状态
  if (!data) return <div className="loading">加载中...</div>;

  // 正常渲染数据内容
  return <div>{JSON.stringify(data)}</div>;
}

export async function getServerSideProps() {
  try {
    // 模拟2-3秒的数据请求
    await new Promise(resolve => setTimeout(resolve, 2500));
    return { props: { data: { content: '服务端获取的数据' } } };
  } catch (e) {
    console.error(e);
    return { props: { data: undefined } };
  }
}

方案二:结合Next.js内置Loading组件(简化客户端跳转)

如果觉得方案一太繁琐,可以用Next.js内置的loading.js组件处理客户端跳转,再配合方案一的初始加载UI覆盖直接打开场景:

在pages目录下创建loading.js:

export default function Loading() {
  return (
    <div style={{
      position: 'fixed',
      inset: 0,
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: '#ffffff',
      zIndex: 9999,
    }}>
      <div>加载中,请稍候...</div>
    </div>
  );
}

这个组件会在客户端通过Link跳转时自动显示,无需手动监听路由事件,但直接打开/刷新页面时不会生效,所以必须配合方案一中的_document.js初始加载UI使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:15:31