
在 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 设计哲学的加载体验。