React路由切换时过渡动画卡顿怎么办?

长孙卫华 阅读 13

大家好,我在用React Router做页面切换过渡动画时遇到问题。我给路由组件加了Animate.css的fadeIn动画,但切换页面时动画有时候会卡顿或者直接跳过。我尝试在useEffect里手动添加类名,但感觉时机没把控好。

代码是这样的:


import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

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

  useEffect(() => {
    const element = document.querySelector('.page-container');
    element?.classList.add('animated', 'fadeIn');
    return () => element?.classList.remove('animated', 'fadeIn');
  }, [location]);

  return <div className="page-container">...</div>;
}

我猜问题可能出在组件卸载太快导致动画没执行完就被销毁了?或者useEffect里的DOM操作有什么隐患?有经验的前辈能指导下更优雅的解决方案吗?

我来解答 赞 1 收藏
二维码
手机扫码查看
2 条解答
利利
利利 Lv1
你遇到的问题确实很常见,主要原因是React组件在路由切换时会直接卸载,而动画还没执行完就被干掉了。CSS的话,动画需要一定的时间来完成,但React默认不会等你动画结束就直接移除DOM,所以会出现卡顿或者跳过的情况。

要解决这个问题,可以考虑用一个专门处理过渡动画的库,比如react-transition-group,它能帮你更好地控制组件的进入和退出动画。具体做法是给路由组件包一层CSSTransition,通过它的生命周期钩子来确保动画完整执行。

下面是一个改进后的代码示例:

import React from 'react';
import { useLocation } from 'react-router-dom';
import { CSSTransition, SwitchTransition } from 'react-transition-group';
import './Page.css'; // 假设你在这里定义了动画样式

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

return (
<SwitchTransition>
<CSSTransition
key={location.pathname}
classNames="page"
timeout={300} // 动画持续时间,和CSS里的时间保持一致
unmountOnExit
>
<div className="page-container">
{/* 这里放你的页面内容 */}
Current Path: {location.pathname}
</div>
</CSSTransition>
</SwitchTransition>
);
}


然后在CSS里定义动画的关键帧,比如这样:

.page-enter {
opacity: 0;
transform: translateX(20px);
}

.page-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}

.page-exit {
opacity: 1;
}

.page-exit-active {
opacity: 0;
transform: translateX(-20px);
transition: opacity 300ms, transform 300ms;
}


这里的关键点是CSSTransition会在组件进入和退出时自动添加对应的类名(比如page-enterpage-enter-active),你可以根据这些类名定义CSS动画。timeout属性要和CSS里定义的动画时间一致,否则可能会出现动画不完整或者卡顿的情况。

至于你之前用useEffect手动操作DOM的方式,其实不是特别推荐,因为React本身的数据流设计就不建议直接操作DOM,容易引发各种问题,比如你提到的时机不对或者动画被跳过。

最后吐槽一句,前端做动画真的挺烦的,尤其是涉及到路由切换的时候,稍微不注意就会显得很卡顿,不过用对工具后会轻松很多。
点赞 2
2026-02-17 09:05
Good“建英
你猜得没错,问题确实出在组件卸载得太快,动画还没完成就被干掉了。React的组件生命周期和动画的执行时机有时候会打架,尤其是像你这样直接操作DOM的时候,很容易出现动画跳过或者卡顿的情况。

可以试试这样,用CSS动画配合React的状态管理来控制动画的完整流程。具体来说,就是给路由切换加一个过渡状态,等动画完成后再真正卸载组件。下面是一个改进的思路:

首先,你需要一个包裹组件来处理动画逻辑。这个组件会在路由切换时先触发动画,等动画结束再卸载旧的页面。

import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function AnimatedPage({ children }) {
const location = useLocation();
const [displayedLocation, setDisplayedLocation] = useState(location);
const [transitionStage, setTransitionStage] = useState('fadeIn');

useEffect(() => {
if (location !== displayedLocation) {
setTransitionStage('fadeOut');
}
}, [location, displayedLocation]);

const onAnimationEnd = () => {
if (transitionStage === 'fadeOut') {
setDisplayedLocation(location);
setTransitionStage('fadeIn');
}
};

return (
<div
className={page-container animated ${transitionStage}}
onAnimationEnd={onAnimationEnd}
>
{children}
</div>
);
}

export default function Page() {
return (
<AnimatedPage>
{/* 页面内容放这里 */}
</AnimatedPage>
);
}


然后在CSS里定义好动画规则,比如:

.page-container {
position: absolute;
width: 100%;
top: 0;
left: 0;
}

.fadeIn {
animation: fadeIn 0.5s ease-in-out;
}

.fadeOut {
animation: fadeOut 0.5s ease-in-out;
}

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}


这里的重点是通过 onAnimationEnd 来监听动画的结束事件,确保动画完整执行后再切换页面。这样就避免了组件被提前卸载的问题。

另外一个小建议,直接操作DOM的方式虽然能跑,但不太好维护,建议尽量用React的状态和生命周期来控制动画。如果以后项目复杂度增加,这种方式会更容易扩展。

对了,如果你觉得手写这些太麻烦,也可以看看 react-transition-group 这个库,它专门用来处理这种过渡动画的场景,封装得很好用。不过自己实现一遍也能加深理解,看你需求吧!
点赞 1
2026-02-16 02:02