React Router/lazy Suspense+useTransition 下页渲染前停留当前页
React Router v5 + React 18 无闪烁懒加载路由实现方案
问题核心
使用React.lazy+React.Suspense实现路由懒加载时,默认配置下路由跳转是紧急更新,会立刻卸载旧页面、显示Suspense fallback,等新页面资源加载完再替换成新页面,必然出现加载闪烁。
要实现的效果:跳转时保留当前已渲染页面,等新页面资源加载、渲染完成后再一次性完成路由切换,完全消除闪烁。
运行环境要求:react v18.2、react-router v5
实现代码
核心思路是利用React 18的useTransition将路由状态更新标记为非紧急过渡更新,过渡期间保留旧UI,直到新页面就绪再替换。
import React, { useState, useTransition, Suspense, lazy, useEffect } from 'react'; import { Router, Route, Switch } from 'react-router-dom'; import { createBrowserHistory } from 'history'; // 初始化独立history实例 const customHistory = createBrowserHistory(); // 路由懒加载声明 const Home = lazy(() => import('./pages/Home')); const About = lazy(() => import('./pages/About')); const UserCenter = lazy(() => import('./pages/UserCenter')); function App() { const [isPending, startTransition] = useTransition(); // 自主托管路由location状态 const [routerLocation, setRouterLocation] = useState(customHistory.location); useEffect(() => { // 监听路由变化,将location更新包裹在startTransition中标记为过渡更新 const unListen = customHistory.listen((updateConfig) => { startTransition(() => { setRouterLocation(updateConfig.location); }); }); return unListen; }, [startTransition]); return ( <Router history={customHistory}> {/* 关键:Suspense fallback设为null,避免过渡期间替换旧页面显示整块加载态 */} <Suspense fallback={null}> {/* 传入自主托管的location给Switch,接管路由渲染判断 */} <Switch location={routerLocation}> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/user" component={UserCenter} /> </Switch> </Suspense> {/* 可选:非侵入式加载提示,不占用页面布局位置,不会造成内容闪烁 */} {isPending && ( <div className="fixed top-0 left-0 h-[2px] w-full bg-blue-500 animate-[loading_1s_ease-in-out_infinite]" /> )} </Router> ); } export default App;
关键注意点
- 包裹路由的
Suspense不能设置整块页面级的fallback,否则会直接覆盖旧页面触发闪烁,fallback设为null即可让React在过渡期间保留旧页面 - React Router v5没有内置React 18过渡特性支持,必须自主托管location状态,把路由更新逻辑放到
startTransition回调中,才能让React识别这是可中断的过渡更新 - 不需要修改原有
Link组件、编程式导航(history.push/history.replace)的写法,根层的history监听会自动接管所有路由跳转逻辑 - 加载提示建议用顶部细进度条、角落小loading标识这类不替换主内容的形式,避免破坏页面连续性
旧方案失效原因
网上流传的早期方案大多是React 18正式版发布前的实现,当时useTransitionAPI行为未稳定,也没有接管React Router内部的location更新逻辑,路由跳转依然属于紧急更新,会立刻卸载旧页面触发fallback,无法解决闪烁问题。
内容的提问来源于stack exchange,提问作者bearsworth
相关产品推荐
相关产品推荐

