You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Router DOM 6.8+中结合Framer Motion实现路由退出动画?

解决createBrowserRouter搭配AnimatePresence实现路由退出动画的问题

在React Router v6+使用createBrowserRouter时,不能直接把AnimatePresence作为路由组件或包裹RouterProvider,正确的做法是在**根布局组件(RootLayout)**中结合useLocation钩子来实现。

核心解决方案步骤

  • 在根布局中通过useLocation获取当前路由的location.key(每个路由切换时这个key会更新,用来触发AnimatePresence的退出逻辑)
  • 用AnimatePresence包裹<Outlet />,并为包裹Outlet的容器添加location.key作为唯一标识
  • 为路由内容添加motion组件及动画属性(initial/animate/exit)

完整代码示例

1. 修改RootLayout组件

import { Outlet, useLocation } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';

function RootLayout() {
  const location = useLocation();

  return (
    <div className="root-layout">
      {/* 这里放置导航栏、页脚等公共布局内容 */}
      <AnimatePresence exitBeforeEnter>
        {/* 用location.key作为key,确保路由切换时触发动画 */}
        <motion.div
          key={location.key}
          initial={{ opacity: 0, x: 20 }}
          animate={{ opacity: 1, x: 0 }}
          exit={{ opacity: 0, x: -20 }}
          transition={{ duration: 0.3 }}
        >
          <Outlet />
        </motion.div>
      </AnimatePresence>
    </div>
  );
}

2. 保持原有路由配置不变

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<RootLayout />}>
      <Route index element={<Main />} />
      <Route path="about" element={<About />} />
      <Route path="projects" element={<Projects />} />
      <Route path="contact" element={<Contact />} />
    </Route>
  )
);

function App() {
  return <RouterProvider router={router} />;
}

为什么之前的尝试失败?

  • 包裹RouterProvider外:RouterProvider是React Router的顶层入口,AnimatePresence无法在这里感知路由变化的唯一标识(key),因此无法触发退出动画。
  • 直接包裹Outlet但未加key:AnimatePresence需要通过唯一key识别组件的挂载/卸载,没有key的话,Outlet切换内容只会更新内部DOM,不会触发退出逻辑。
  • 单独使用motion+useLocation但未包AnimatePresence:exit动画属性必须在AnimatePresence容器内才能生效,它专门负责监听组件的卸载并执行退出动画。

扩展:自定义单页动画

如果需要给不同页面设置不同的动画效果,可以把motion.div放到每个路由组件内部,RootLayout只负责提供AnimatePresence容器:

示例:Main组件自定义动画

import { motion } from 'framer-motion';

function Main() {
  return (
    <motion.div
      initial={{ opacity: 0, scale: 0.9 }}
      animate={{ opacity: 1, scale: 1 }}
      exit={{ opacity: 0, scale: 0.9 }}
      transition={{ duration: 0.4 }}
    >
      {/* 主页内容 */}
    </motion.div>
  );
}

对应修改RootLayout

function RootLayout() {
  const location = useLocation();

  return (
    <div className="root-layout">
      <AnimatePresence exitBeforeEnter>
        <div key={location.key}>
          <Outlet />
        </div>
      </AnimatePresence>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者Wise.Wolf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 16:25:24