
在 react 单页应用(spa)中,直接混用 jquery 的 `$(window).on('load')` 无法响应路由跳转,导致预加载器不消失;应改用 react 生命周期或路由事件监听 + 状态控制来统一管理显示与隐藏逻辑。
在 React 中实现预加载动画(Preloader),关键在于理解 SPA 的运行机制:页面不会真正“刷新”,HTML 文档不会重新加载,因此原生 window.load 或 jQuery 的 $(window).on('load') 仅在首次完整加载时触发,后续路由跳转(如点击 Home 链接)不会再次触发该事件。你当前代码中混合使用 jQuery 与 React(如 useEffect + document.querySelector),不仅破坏了 React 的声明式更新原则,还易引发 DOM 状态不一致、CSS 丢失、重复挂载等问题。
✅ 正确做法:使用 React 状态 + 路由监听(推荐 react-router-dom v6+)实现可控的 Preloader:
// Preloader.tsx
import { useState, useEffect } from 'react';
import { useLocation, useNavigation } from 'react-router-dom';
export const Preloader = () => {
const [isVisible, setIsVisible] = useState(true);
const location = useLocation();
const navigation = useNavigation();
// 监听路由跳转开始(navigation.state === 'loading')
useEffect(() => {
if (navigation.state === 'loading') {
setIsVisible(true); // 进入新路由时显示加载器
}
}, [navigation.state]);
// 监听路由跳转完成(location 变化后延迟隐藏,确保过渡自然)
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(false);
}, 300); // 匹配 CSS fade-out 动画时长(如 300ms)
return () => clearTimeout(timer);
}, [location]);
if (!isVisible) return null;
return (
);
};? 对应 CSS(确保支持平滑淡出):
.preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.preloader:not(:root) {
opacity: 1;
visibility: visible;
}
.preloader.hidden {
opacity: 0;
visibility: hidden;
}⚠️ 注意事项:
- ❌ 不要手动操作 DOM(如 document.querySelector(...).style.display = "none"),这会绕过 React 渲染流程,导致样式丢失或状态不同步;
- ❌ 避免在组件中引入 jQuery,React 的虚拟 DOM 与 jQuery 的直接 DOM 操作存在生命周期冲突;
- ✅ 若使用 BrowserRouter,确保 挂载在 Router 内部且位置合理(通常置于 App 根组件顶层);
- ✅ 如需更精细控制(如仅对异步数据加载显示 Preloader),可结合 Suspense + lazy 或自定义 useAsyncLoader Hook。
总结:Preloader 在 React 中不是“一次性的页面加载装饰”,而是受路由状态驱动的 UI 组件。用状态 + 路由钩子替代全局事件监听,才能真正实现响应式、可维护、符合 React 设计哲学的加载体验。










