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
相关产品推荐
相关产品推荐

