Next.js中实现页面加载状态指示器的通用方案是什么?
Next.js页面跳转加载状态指示器实现方案
在Next.js中,页面跳转时如果加载耗时较长,缺乏状态提示会直接影响用户体验,下面是几种实用的落地方案:
1. Pages Router:利用Router事件全局监听
在Pages Router的_app.js中,通过监听Next.js Router的路由切换事件,统一控制加载状态的显示:
import { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; import LoadingSpinner from '../components/LoadingSpinner'; function MyApp({ Component, pageProps }) { const [isLoading, setIsLoading] = useState(false); const router = useRouter(); useEffect(() => { const handleStart = () => setIsLoading(true); const handleComplete = () => setIsLoading(false); const handleError = () => setIsLoading(false); router.events.on('routeChangeStart', handleStart); router.events.on('routeChangeComplete', handleComplete); router.events.on('routeChangeError', handleError); return () => { router.events.off('routeChangeStart', handleStart); router.events.off('routeChangeComplete', handleComplete); router.events.off('routeChangeError', handleError); }; }, [router]); return ( <> {isLoading && <LoadingSpinner />} <Component {...pageProps} /> </> ); } export default MyApp;
说明:这种方式可以覆盖所有路由跳转场景,适合需要统一加载提示的项目。LoadingSpinner是自定义的加载组件,比如居中的旋转图标或全屏遮罩。
2. App Router:使用内置的loading.js(Next.js 13+)
Next.js 13+的App Router提供了原生加载状态解决方案,只需在目标路由目录下创建loading.js文件,页面加载时会自动渲染该组件:
// app/words/loading.js export default function Loading() { return ( <div className="flex justify-center items-center h-screen"> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div> <p className="ml-3">正在加载单词列表...</p> </div> ); }
说明:无需手动监听路由,Next.js会自动处理加载状态的切换,支持流式渲染,是App Router下的最优解。可在不同路由目录创建专属loading组件,也可在根目录设置全局加载提示。
3. 自定义带加载状态的跳转组件
如果需要针对特定跳转做定制化反馈,可以封装自己的跳转组件,点击时立即触发加载状态:
import { useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/router'; import LoadingButton from '../components/LoadingButton'; export default function CustomLink({ href, children }) { const [isLoading, setIsLoading] = useState(false); const router = useRouter(); const handleClick = async (e) => { setIsLoading(true); try { await router.push(href); } catch (err) { console.error('跳转失败:', err); } finally { setIsLoading(false); } }; return isLoading ? ( <LoadingButton>{children}</LoadingButton> ) : ( <Link href={href} onClick={handleClick}> {children} </Link> ); }
说明:适合需要点击后立即反馈的场景,比如你提到的“查找所有以a开头的单词”按钮,点击后可直接切换为加载状态按钮,避免用户重复操作。
4. 全局顶部进度条
如果想要更细腻的加载感知,可以实现一个顶部进度条:
// _app.js (Pages Router) import { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; function MyApp({ Component, pageProps }) { const [progress, setProgress] = useState(0); const router = useRouter(); useEffect(() => { const handleStart = () => { setProgress(30); const timer = setInterval(() => { setProgress(prev => prev < 90 ? prev + 10 : 90); }, 100); return () => clearInterval(timer); }; const handleComplete = () => { setProgress(100); setTimeout(() => setProgress(0), 300); }; router.events.on('routeChangeStart', handleStart); router.events.on('routeChangeComplete', handleComplete); router.events.on('routeChangeError', handleComplete); return () => { router.events.off('routeChangeStart', handleStart); router.events.off('routeChangeComplete', handleComplete); router.events.off('routeChangeError', handleComplete); }; }, [router]); return ( <> {progress > 0 && ( <div className="fixed top-0 left-0 right-0 h-1 bg-blue-500 z-50" style={{ width: `${progress}%`, transition: 'width 0.2s ease' }}></div> )} <Component {...pageProps} /> </> ); } export default MyApp;
说明:顶部进度条能给用户更直观的加载节奏感知,避免用户误以为页面无响应。
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

