如何在NextJS页面首次加载(非路由切换)时显示预加载器?
在Next.js中实现页面首次加载时的加载器
核心思路
页面首次加载(刷新、新开标签页访问)时,Next.js Router不会触发路由切换事件,所以得从页面初始化阶段入手,通过React状态管理结合页面加载/数据加载的时机来控制加载器的显示与隐藏。
Pages Router 实现方案
1. 全局_app.jsx/tsx 统一处理
在全局入口文件中维护加载状态,监听页面资源加载完成事件:
// pages/_app.jsx import { useState, useEffect } from 'react'; import '../styles/globals.css'; function MyApp({ Component, pageProps }) { const [isLoading, setIsLoading] = useState(true); useEffect(() => { const handlePageLoad = () => setIsLoading(false); // 若页面已加载完成,直接关闭加载器 if (document.readyState === 'complete') { handlePageLoad(); } else { window.addEventListener('load', handlePageLoad); return () => window.removeEventListener('load', handlePageLoad); } }, []); if (isLoading) { // 自定义加载器组件,可替换为动画效果 return ( <div style={{ position: 'fixed', inset: 0, display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#fff' }}> <div>加载中...</div> </div> ); } return <Component {...pageProps} />; } export default MyApp;
2. 结合数据加载时机控制
如果页面依赖服务端数据,可在页面组件中结合数据加载完成状态关闭加载器:
// pages/index.jsx import { useState, useEffect } from 'react'; export async function getServerSideProps() { // 模拟服务端数据请求延迟 await new Promise(resolve => setTimeout(resolve, 1200)); return { props: { content: '首页内容' } }; } export default function Home({ content }) { const [isLoading, setIsLoading] = useState(true); useEffect(() => { // 数据就绪后关闭加载器 setIsLoading(false); }, [content]); if (isLoading) { return <div className="loader">加载中...</div>; } return <div>{content}</div>; }
App Router 实现方案
1. Root Layout 全局处理
在根布局中添加全局加载器,覆盖首次加载场景:
// app/layout.jsx import { useState, useEffect } from 'react'; import './globals.css'; export default function RootLayout({ children }) { const [isFirstLoad, setIsFirstLoad] = useState(true); useEffect(() => { const checkLoadStatus = () => { if (document.readyState === 'complete') { setIsFirstLoad(false); } }; checkLoadStatus(); window.addEventListener('load', checkLoadStatus); return () => window.removeEventListener('load', checkLoadStatus); }, []); return ( <html lang="zh-CN"> <body> {isFirstLoad ? ( <div className="global-loader"> <span>正在初始化页面...</span> </div> ) : ( children )} </body> </html> ); }
2. 单个页面独立处理
仅需特定页面显示首次加载器时,直接在对应page.jsx中实现:
// app/page.jsx import { useState, useEffect } from 'react'; export default function Home() { const [isLoading, setIsLoading] = useState(true); useEffect(() => { // 可替换为数据加载完成的回调,而非固定延迟 const timer = setTimeout(() => setIsLoading(false), 600); return () => clearTimeout(timer); }, []); if (isLoading) { return <div className="page-loader">加载中...</div>; } return <main>首页内容</main>; }
优化建议
- 避免固定延迟:优先用
window.load事件(等待所有资源加载完成)或数据请求的完成状态来切换加载器,减少不必要的等待。 - 平滑过渡效果:给加载器添加CSS过渡,提升体验:
.global-loader { position: fixed; inset: 0; display: flex; justify-content: center; align-items: center; background: #fff; transition: opacity 0.3s ease; } .global-loader.hidden { opacity: 0; pointer-events: none; }
- 结合Suspense:App Router中可搭配React Suspense处理组件级加载,但全局首次加载的加载器仍需依赖状态管理。
内容的提问来源于stack exchange,提问作者Grosu Cristi-Alexandru
相关产品推荐
相关产品推荐

