使用React Router v6 Link时每次导航均显示带最小延迟的加载状态
解决React Router v6中懒加载页面重复导航不显示加载状态的问题
问题原因
React的lazy函数会缓存已加载的组件模块,首次访问时触发pMinDelay的延迟加载逻辑并显示Suspense的fallback,但后续导航时模块已存在于缓存中,不会再触发加载流程,导致加载状态和延迟失效。
解决方案
通过监听路由变化,强制每次导航到目标页面时都显示至少500ms的加载状态,同时保留组件缓存避免重复加载模块。
步骤1:封装延迟加载容器组件
这个组件会监听路由的location.key变化(每次导航都会生成新的key),触发加载状态并维持至少500ms:
import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; // 自定义Loading组件 const LoadingSpinner = () => ( <div style={{ padding: '2rem', textAlign: 'center' }}> <span>加载中...</span> </div> ); const DelayedPageWrapper = ({ children }: { children: React.ReactNode }) => { const [isLoading, setIsLoading] = useState(true); const location = useLocation(); useEffect(() => { // 每次路由切换时重置加载状态 setIsLoading(true); const timer = setTimeout(() => { setIsLoading(false); }, 500); // 组件卸载时清除定时器 return () => clearTimeout(timer); }, [location.key]); if (isLoading) { return <LoadingSpinner />; } return <>{children}</>; };
步骤2:保留原有懒加载配置
继续使用lazy和pMinDelay处理首次加载的延迟:
import { lazy } from 'react'; import pMinDelay from 'p-min-delay'; const Page1 = lazy(() => pMinDelay(import('./Page1'), 500)); const Page2 = lazy(() => pMinDelay(import('./Page2'), 500));
步骤3:路由配置整合
将懒加载页面用DelayedPageWrapper包裹,同时保留Suspense处理首次加载的异步逻辑:
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; import { Suspense } from 'react'; function App() { return ( <Router> <nav style={{ margin: '1rem 0' }}> <Link to="/page1" style={{ marginRight: '1rem' }}>Page1</Link> <Link to="/page2">Page2</Link> </nav> {/* Suspense处理首次加载的异步组件 */} <Suspense fallback={<LoadingSpinner />}> <Routes> <Route path="/page1" element={ <DelayedPageWrapper> <Page1 /> </DelayedPageWrapper> } /> <Route path="/page2" element={ <DelayedPageWrapper> <Page2 /> </DelayedPageWrapper> } /> </Routes> </Suspense> </Router> ); } export default App;
效果说明
- 首次访问页面:
Suspense结合pMinDelay保证至少500ms的加载状态,若组件实际加载时间超过500ms,会持续显示到加载完成。 - 再次导航页面:
DelayedPageWrapper监听路由变化,强制显示500ms加载状态,组件不会重新挂载(状态保留),也不会重复加载模块。 - 完全符合单页应用要求,无需使用
reloadDocument刷新整个应用。
内容的提问来源于stack exchange,提问作者Renars Kilis
相关产品推荐
相关产品推荐

