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

