React(MUI)桌面页面正常加载,移动端仅显示Loader组件求助
问题:React + MUI 页面移动端始终停留在Loader,桌面端正常
我有一个使用MUI组件的React页面,主App代码如下:
// Theme and other components import React, { useEffect, useState, lazy, Suspense, MouseEvent } from 'react'; import Box from "@mui/material/Box"; import { createTheme, ThemeProvider } from "@mui/material"; import ErrorBoundary from "../ErrorHandler/ErrorBoundary"; import Loader from "../Components/HelperComps/Loader"; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; // Header / Footer import ResponsiveAppBar from "../Components/HeaderFooter/ResponsiveAppBar"; import Footer from "../Components/HeaderFooter/Footer"; import WaterEffect from "../Components/HelperComps/WaterEffect/WaterEffect"; import {BANANA_CURSOR, MUSIC_MOUSE} from "../Stuff/ImagePaths"; // Pages (lazy loading) const Home = lazy(() => import("../Components/Pages/Home")); const Live = lazy(() => import("../Components/Pages/Live")); const Studio = lazy(() => import("../Components/Pages/Studio")); const Aktuell = lazy(() => import("../Components/Pages/Zeit/Aktuell")); const Vergangenheit = lazy(() => import("../Components/Pages/Zeit/Vergangenheit")); const Zukunft = lazy(() => import("../Components/Pages/Zeit/Zukunft")); const Genesis = lazy(() => import("../Components/Pages/Zeit/Genesis")); const Kontakt = lazy(() => import("../Components/HeaderFooter/FooterSites/Kontakt")); const Datenschutz = lazy(() => import("../Components/HeaderFooter/FooterSites/Datenschutz")); const Impressum = lazy(() => import("../Components/HeaderFooter/FooterSites/Impressum")); const ErrorPage = lazy(() => import("../ErrorHandler/ErrorPage")); // create a theme instance const theme = createTheme({ components: { MuiAppBar: { styleOverrides: { root: { height: '9vh', }, }, }, }, }); interface MousePosition { x: number; y: number; } const App: React.FC = () => { const [loading, setLoading] = useState<boolean>(true); const [mousePos, setMousePos] = useState<MousePosition>({ x: 0, y: 0 }); useEffect(() => { const minLoadingTime = 1000; // 1 second minimum loading time const startTime = Date.now(); const handleLoad = () => { const elapsedTime = Date.now() - startTime; const remainingTime = minLoadingTime - elapsedTime; if (remainingTime > 0) { setTimeout(() => { setLoading(false); }, remainingTime); } else { setLoading(false); } }; // Adding event listener for when the entire page has loaded window.addEventListener('load', handleLoad); // Cleanup event listener on component unmount return () => { window.removeEventListener('load', handleLoad); }; }, []); const handleMouseMove = (event: MouseEvent<HTMLDivElement>) => { setMousePos({ x: event.clientX + window.scrollX, y: event.clientY + window.scrollY }); }; return ( <ThemeProvider theme={theme}> <ErrorBoundary> <Router> <Box // onMouseMove={handleMouseMove} sx={{ minHeight: '100 vh', paddingBottom: '9vh', paddingTop: '9vh', position: 'relative', overflow: 'hidden', cursor: `url(${MUSIC_MOUSE}) 16 16, auto`, /*'&::after': { content: '""', width: 100, height: 100, background: 'radial-gradient(circle, rgba(255, 255, 0, 1) 0%, rgba(255, 255, 0, 0.8) 25%, rgba(255, 255, 0, 0.4) 50%, rgba(255, 255, 0, 0.2) 75%, rgba(255, 255, 0, 0) 100%)', borderRadius: '50%', position: 'absolute', top: mousePos.y, left: mousePos.x, pointerEvents: 'none', // Ensure the effect doesn't interfere with pointer events transform: 'translate(-50%, -50%)', // Center the effect exactly at the cursor },*/ }} > {loading && <Loader />} {!loading && ( <> <ResponsiveAppBar /> <Suspense fallback={<Loader />}> <Routes> <Route path="/" element={<Home />} /> <Route path="/zeit/genesis" element={<Genesis />} /> <Route path="/zeit/vergangenheit" element={<Vergangenheit />} /> <Route path="/zeit/aktuell" element={<Aktuell />} /> <Route path="/zeit/zukunft" element={<Zukunft />} /> <Route path="/studio" element={<Studio />} /> <Route path="/live" element={<Live />} /> <Route path="/kontakt" element={<Kontakt />} /> <Route path="/datenschutz" element={<Datenschutz />} /> <Route path="/impressum" element={<Impressum />} /> <Route path="*" element={<ErrorPage errorCode="Error Code: 418" errorMessage="I'm a Teapot; #(RFC 2324, RFC 7168)" />} /> </Routes> </Suspense> <Footer /> </> )} </Box> </Router> </ErrorBoundary> </ThemeProvider> ); } export default App;
页面在桌面端可正常加载,但在智能手机上始终仅显示Loader加载组件,无法显示Home页面。已尝试替换为极简的Hello World版Home组件测试,也移除了之前嵌入的自定义字体,但问题仍未解决,求排查思路或代码错误分析。
排查思路&代码错误分析
1. 修复CSS语法错误
代码中Box组件的minHeight: '100 vh'存在空格,正确写法应为'100vh'。桌面端浏览器可能容错,但移动端浏览器解析异常可能导致布局阻塞,直接影响组件渲染:
sx={{ minHeight: '100vh', // 去掉100和vh之间的空格 // ...其他样式 }}
2. 修正window.load事件的移动端兼容性
移动端浏览器的load事件触发逻辑和桌面端有差异,部分场景下可能不会触发(如缓存、WebView环境)。建议替换为兼容性更好的DOMContentLoaded,同时做兜底处理:
useEffect(() => { const minLoadingTime = 1000; const startTime = Date.now(); const finishLoading = () => { const elapsedTime = Date.now() - startTime; setTimeout(() => setLoading(false), Math.max(0, minLoadingTime - elapsedTime)); }; // 页面已就绪则直接执行,否则监听事件 if (document.readyState === 'complete' || document.readyState === 'interactive') { finishLoading(); } else { document.addEventListener('DOMContentLoaded', finishLoading); window.addEventListener('load', finishLoading); // 兜底 } return () => { document.removeEventListener('DOMContentLoaded', finishLoading); window.removeEventListener('load', finishLoading); }; }, []);
3. 检查ErrorBoundary的错误捕获
外层的ErrorBoundary可能捕获了移动端特有的错误但未暴露。可以临时修改ErrorBoundary组件,让它在捕获错误时打印控制台日志,或者直接显示错误内容,同时用Chrome远程调试移动端页面查看报错信息。
4. 排查自定义光标资源的影响
移动端浏览器基本不支持自定义光标,若MUSIC_MOUSE资源路径在移动端加载失败,可能阻塞页面渲染。可以临时注释掉cursor属性测试:
// cursor: `url(${MUSIC_MOUSE}) 16 16, auto`,
5. 测试懒加载的兼容性
虽然主流移动端浏览器支持动态import,但仍可尝试临时去掉懒加载,直接导入Home组件验证:
// 替换懒加载为直接导入 import Home from "../Components/Pages/Home"; // 同时可以去掉Suspense包裹,或保留但测试
6. 验证路由模式
移动端可能存在history模式的兼容性问题,可临时切换为HashRouter测试:
import { HashRouter as Router, Route, Routes } from 'react-router-dom';
内容的提问来源于stack exchange,提问作者Riemann
相关产品推荐
相关产品推荐

