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

如何修复React Router v4页面过渡动画重复显示Bug?

我之前也碰到过这个一模一样的问题!核心原因是 TransitionGroup 在路由切换时会同时挂载新旧两个路由组件,直到旧组件的退出动画完成,这就导致了视觉上的重叠。用 fixed 确实能快速解决重叠,但代价是破坏了正常的文档流,动态高度的组件自然会把 Footer 挤乱。下面给你几个靠谱的解决思路:

方案一:优化 react-transition-group 的布局(不破坏文档流)

这个思路的核心是用相对定位的容器包裹路由组件,配合 CSS 控制过渡时的组件定位,既解决重叠问题,又保留正常的文档流让 Footer 位置正确。

组件结构代码

import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { Switch, Route, withRouter } from 'react-router-dom';

// 用withRouter包裹获取location对象
const App = withRouter(({ location }) => (
  <div className="app-container">
    {/* 路由过渡容器,负责占满除Footer外的空间 */}
    <div className="route-transition-wrapper">
      <TransitionGroup>
        {/* 必须给CSSTransition设置唯一key,这里用location.key保证每次路由切换都触发过渡 */}
        <CSSTransition
          key={location.key}
          timeout={500} // 动画时长,要和CSS里的transition时长一致
          classNames="fade"
          unmountOnExit // 旧组件退出动画完成后自动卸载,避免冗余DOM
        >
          {/* 必须把location传给Switch,否则TransitionGroup无法正确识别路由变化 */}
          <Switch location={location}>
            <Route path="/" exact component={Home} />
            <Route path="/about" component={About} />
            <Route path="/contact" component={Contact} />
          </Switch>
        </CSSTransition>
      </TransitionGroup>
    </div>
    {/* Footer放在容器外面,跟着文档流自动定位到底部 */}
    <footer className="app-footer">我是不会乱跑的Footer</footer>
  </div>
));

export default App;

对应的CSS样式

/* 让整个应用容器占满视口,用flex布局控制Footer位置 */
.app-container {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* 路由过渡容器占满除Footer外的所有空间 */
.route-transition-wrapper {
  flex: 1;
  position: relative; /* 关键:让内部绝对定位的组件相对于这个容器 */
  overflow: hidden; /* 防止过渡时组件内容溢出容器 */
}

/* 淡入淡出动画的CSS类,和CSSTransition的classNames对应 */
.fade-enter {
  opacity: 0;
  position: absolute; /* 新组件绝对定位在容器内,覆盖旧组件 */
  top: 0;
  left: 0;
  right: 0;
}

.fade-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in-out;
}

.fade-exit {
  opacity: 1;
}

.fade-exit-active {
  opacity: 0;
  transition: opacity 500ms ease-in-out;
}

解释一下:这里用flex布局让app-container占满整个视口,route-transition-wrapper通过flex:1自动填充除Footer外的空间。给容器设置position:relative后,新进入的路由组件用position:absolute只会相对于这个容器定位,不会脱离整个文档流,Footer自然能留在底部,不会被覆盖。unmountOnExit属性则会在旧组件的退出动画完成后立即卸载它,避免页面上存在冗余的DOM节点。

方案二:用 framer-motion 简化路由过渡

如果你觉得手动写CSS和布局太麻烦,可以试试framer-motion这个动画库,它处理路由过渡的逻辑更简洁,还能自动处理组件的挂载时机,完全不用操心重叠问题。

组件结构代码

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

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

  return (
    <div className="app-container">
      {/* AnimatePresence负责管理组件的进入/退出动画 */}
      <AnimatePresence mode="wait">
        {/* 给Switch设置key,确保路由切换时触发动画 */}
        <Switch location={location} key={location.pathname}>
          <Route path="/" exact>
            <Home 
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.5 }}
            />
          </Route>
          <Route path="/about">
            <About 
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.5 }}
            />
          </Route>
        </Switch>
      </AnimatePresence>
      <footer className="app-footer">我是不会乱跑的Footer</footer>
    </div>
  );
}

export default App;

基础CSS(只需要处理Footer布局)

.app-container {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* 路由组件容器占满剩余空间 */
.route-content {
  flex: 1;
}

.app-footer {
  padding: 1rem;
  background: #f5f5f5;
}

解释:AnimatePresence的mode="wait"属性会等待旧组件的退出动画完全完成后,再挂载新组件,从根源上避免了新旧组件的重叠问题。而且所有组件都保持正常的文档流布局,Footer的位置完全不用额外调整,非常省心。

最后总结

如果坚持用react-transition-group,方案一完全可以解决你的问题,既保留淡入淡出效果,又不会让Footer错乱;如果想简化开发流程,framer-motion是更好的选择,它的动画能力更强,代码也更简洁。

内容的提问来源于stack exchange,提问作者user10013590

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:45:48