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

Next.js Pages路由中加载状态的最佳实现方案咨询

在Next.js Pages路由中实现加载状态的解决办法

你遇到的核心问题是:getServerSideProps在服务器端处理数据的这段时间,浏览器会一直显示空白,而你用的dynamic组件loading属性,只负责客户端组件加载阶段的占位,覆盖不了服务器端数据查询的等待时间。下面给你几个实际可行的解决方案:

1. 先优化服务器端数据查询的速度

这是解决根源问题的关键,把getServerSideProps的执行时间压下去:

  • 给数据库加索引:比如给Product集合的createdAt字段建索引,加速排序操作;如果经常通过category关联查询,也优化下关联字段的索引。
  • 精简populate返回的字段:别把category的所有数据都拿回来,只取你需要的,比如name、slug,改成这样:
    .populate({
      path: "category",
      model: Category,
      select: "name slug" // 只获取需要的字段
    })
    
  • 加缓存:如果商品数据不是实时更新的,用Redis把查询结果缓存起来,下次请求直接读缓存,跳过数据库查询。

2. 让页面先返回骨架屏HTML

如果没法大幅缩短服务器端的处理时间,就让服务器先返回带骨架屏的页面,别让用户看空白:

  • 不用依赖dynamic的loading,直接在页面组件里处理。另外确保dynamic导入的组件支持SSR(默认是开启的,显式声明更稳妥):
import dynamic from 'next/dynamic';
import Skeleton from '你的骨架屏组件路径';

const Main = dynamic(() => import("../components/home/main"), {
  ssr: true,
  loading: () => (
    <div className="mt-[10px]">
      <Skeleton
        highlightColor="#9ca3af"
        baseColor="#E3E0F3"
        className="h-[430px]"
      />
    </div>
  ),
});

export default function HomePage({ products }) {
  return (
    <div>
      {/* 页面其他固定内容,比如导航栏 */}
      <Main products={products} />
    </div>
  );
}

// 优化后的getServerSideProps
export async function getServerSideProps() {
  db.connectDb();
  let products = await Product.find()
    .sort({ createdAt: -1 })
    .populate({
      path: "category",
      model: Category,
      select: "name slug"
    })
    .lean();

  return {
    props: {
      products: JSON.parse(JSON.stringify(products)),
    },
  };
}

如果你的Main组件用了浏览器专属API(比如window)导致没法SSR,那就在页面里先渲染服务器能生成的骨架屏,等客户端加载完组件再替换。

3. 改用客户端异步获取数据(搭配SWR/React Query)

如果允许客户端拿数据,放弃getServerSideProps,用SWR或者React Query,这样页面初始就能显示骨架屏,用户不会看空白:

import useSWR from 'swr';
import Skeleton from '你的骨架屏组件路径';
import Main from "../components/home/main";

// 数据请求函数
const fetcher = async () => {
  const res = await fetch('/api/products');
  return res.json();
};

export default function HomePage() {
  const { data: products, error } = useSWR('/api/products', fetcher);

  if (error) return <div>加载失败了,试试刷新?</div>;
  // 没拿到数据的时候显示骨架屏
  if (!products) return (
    <div className="mt-[10px]">
      <Skeleton
        highlightColor="#9ca3af"
        baseColor="#E3E0F3"
        className="h-[430px]"
      />
    </div>
  );

  return <Main products={products} />;
}

然后新建pages/api/products.js来处理数据查询:

export default async function handler(req, res) {
  db.connectDb();
  let products = await Product.find()
    .sort({ createdAt: -1 })
    .populate({
      path: "category",
      model: Category,
      select: "name slug"
    })
    .lean();

  res.status(200).json(JSON.parse(JSON.stringify(products)));
}

这种方式还能利用SWR的缓存、后台重新验证等功能,提升用户体验。

4. 加个全局加载进度条

用NProgress给页面切换和数据加载加个顶部进度条,让用户知道“正在加载中”,减少等待焦虑:

  • 先安装:npm install nprogress
  • 在pages/_app.js里配置:
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import Router from 'next/router';

// 监听路由事件
Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:22:49