React Router v6结合Framer Motion保留路由退出动画方案问询
React Router v6 + Framer Motion v6 路由退出动画实现方案
问题背景
使用React Router v6的createBrowserRouter(支持data loader)结合Framer Motion v6时,无法通过直接在路由element外层包裹AnimatePresence实现路由间的退出动画保留效果;改用旧版BrowserRouter虽能实现动画,但无法使用loader功能。
解决方案核心思路
将AnimatePresence移至布局组件(Layout)内部,包裹Outlet组件,同时为路由内容容器添加唯一标识key(基于当前路由路径),让Framer Motion能识别路由切换并触发动画。
具体实现步骤
1. 调整根路由结构
移除根路由element中的AnimatePresence,保持路由结构对loader的支持:
function App() { const router = createBrowserRouter( createRoutesFromElements( <> <Route path="/" element={ <PrivateRoutes> <ScrollToTop> <Layout /> {/* 这里不再包裹AnimatePresence */} </ScrollToTop> </PrivateRoutes> } > {/* 子路由保持不变 */} <Route index element={<Dashboard />} /> <Route path="/transactions" element={<Transactions />} loader={({ params }) => { console.log("PARAMS:", params); return { hello: "world" }; }} /> {/* 其他子路由... */} </Route> <Route path="*" element={<NotFound />} /> <Route path="/auth" element={<PublicRoutes />}> <Route path="login" element={<Login />} /> <Route path="reset-password" element={<ResetPassword />} /> </Route> </> ) ); return <RouterProvider router={router} />; }
2. 修改Layout组件,添加AnimatePresence包裹Outlet
在Layout中引入useLocation获取当前路由路径,作为动画容器的key,确保路由切换时触发Framer Motion的动画逻辑:
import { Outlet, useLocation } from 'react-router-dom'; import { AnimatePresence, motion } from 'framer-motion'; function Layout() { const location = useLocation(); return ( <div className="your-layout-wrapper"> {/* 这里放置你的布局固定元素:侧边栏、顶部导航等 */} <div className="content-area"> <AnimatePresence mode="wait"> {/* 用location.pathname作为唯一key,确保路由变化时触发动画 */} <motion.div key={location.pathname} initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} transition={{ duration: 0.3 }} className="route-content-container" > <Outlet /> {/* 渲染当前路由的内容 */} </motion.div> </AnimatePresence> </div> </div> ); }
3. 自定义路由组件的动画(可选)
如果需要为特定路由设置差异化动画,可以在对应组件外层包裹motion.div,覆盖默认动画:
// Dashboard组件示例 import { motion } from 'framer-motion'; function Dashboard() { return ( <motion.div initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }} transition={{ duration: 0.4 }} > {/* 组件内容 */} </motion.div> ); }
关键注意点
AnimatePresence必须直接包裹会被卸载/挂载的元素,这里Outlet渲染的路由内容正好符合这个要求mode="wait"属性确保退出动画完全执行后,再卸载旧路由组件、挂载新组件,避免动画被中断- 若存在多层嵌套路由,可在对应层级的布局组件中重复此逻辑,实现嵌套路由的层级动画效果
内容的提问来源于stack exchange,提问作者Rainy_Frog11
相关产品推荐
相关产品推荐

