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

Next.js 13(App目录)加载页实现问题求助

解决Next.js App目录模式下加载页不显示问题

我是Next.js新手,采用App目录模式开发大型网站,为提升用户体验需实现加载页。已创建Loading组件,先后尝试通过useState+useEffect、React Suspense两种方式配置,但加载页始终不显示且无报错,相关代码如下:

初始Layout代码

import "./globals.css";
import React, { useState, useEffect } from "react";
import Loading from "../components/loading/loading";

const Layout = ({ children, dataLoaded }) => {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (dataLoaded) {
      setLoading(false);
    }
  }, [dataLoaded]);

  return (
    <body className="app {oswald.className}">
      {loading && <Loading />}
      {children}
    </body>
  );
};

export default Layout;

尝试方案1代码

layout.jsx

import "./globals.css";
import { Suspense } from "react";
import Loading from "../components/loading/loading";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head />
      <body>
        <Suspense fallback={<Loading />}>{children}</Suspense>
      </body>
    </html>
  );
}

LoadingPage.js

const LoadingPage = () => {
  return (
    <div className="loading w-screen h-screen bg-red-100">
      <p>Loading...</p>
    </div>
  );
};

export default LoadingPage;

Loading.js

import LoadingPage from "@/components/loading/loading";

export default function Loading() {
  return <LoadingPage />;
}

问题分析

  1. 初始Layout的问题:App目录的RootLayout必须包含<html>和<head>标签,你提供的初始Layout直接返回<body>,不符合Next.js规范,大概率无法被正确识别;同时dataLoaded是自定义props,但App目录的Layout不会自动传入该参数,导致useEffect无法触发;另外即使loading为true,children会直接渲染在Loading组件之后,若children有背景色会覆盖Loading。

  2. Suspense方案的问题:React Suspense仅在组件触发Suspense边界时才会显示fallback内容,即当子组件存在异步操作(比如Server Component中的await数据请求、客户端组件的lazy动态导入)时才会生效。如果你的页面/组件都是同步渲染,Suspense不会触发,自然不会显示Loading。

解决方案

方案1:使用App目录内置的loading.js(推荐)

Next.js App目录提供了原生加载页支持,无需手动管理状态或Suspense:

  • 在app目录下创建loading.jsx文件,直接导出加载组件:
// app/loading.jsx
export default function Loading() {
  return (
    <div className="loading w-screen h-screen bg-red-100 fixed top-0 left-0 z-50 flex items-center justify-center">
      <p>Loading...</p>
    </div>
  );
}

Next.js会自动在页面路由切换、数据加载过程中显示该加载页,加载完成后自动隐藏。

方案2:正确使用Suspense

如果需要手动控制Suspense,必须确保子组件存在异步操作:

  1. 在页面组件中添加异步数据请求(以Server Component为例):
// app/page.jsx
async function fetchData() {
  // 模拟异步数据请求
  await new Promise(resolve => setTimeout(resolve, 2000));
  return { content: "页面内容" };
}

export default async function Home() {
  const data = await fetchData();
  return <div>{data.content}</div>;
}
  1. 此时RootLayout中的Suspense会触发,显示fallback的Loading组件。

方案3:修复初始的useState+useEffect方案

  • 确保Layout是符合规范的RootLayout(包含<html>和<head>);
  • 手动控制加载状态,给Loading组件添加固定定位避免被覆盖:
// app/layout.jsx
import "./globals.css";
import React, { useState, useEffect } from "react";
import Loading from "@/components/loading/loading";

export default function RootLayout({ children }) {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 页面加载完成后隐藏加载页
    const handleLoad = () => setLoading(false);
    window.addEventListener("load", handleLoad);
    return () => window.removeEventListener("load", handleLoad);
  }, []);

  return (
    <html lang="en">
      <head />
      <body>
        {loading && (
          <div className="fixed top-0 left-0 w-screen h-screen bg-red-100 z-50 flex items-center justify-center">
            <p>Loading...</p>
          </div>
        )}
        {children}
      </body>
    </html>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:25:19