React.js动态路由刷新BasicLayout页面出现空白问题求助
核心问题排查
刷新页面后页面空白,主要由以下几个原因导致:
1. BasicLayout 中 Menu 组件的 location 属性错误
在 react-router-dom v6 中,路由组件不会自动接收 location 作为 props,你在 Menu 中传入的 props.location 是 undefined,引发组件渲染错误,导致页面空白。
2. 权限校验逻辑错误
checkRouterPower 函数将路径拆分为片段逐一校验,但你的菜单权限存储的是完整路径(如 /analytic/alertNdr),拆分后的片段(analytic、alertNdr)无法匹配完整路径,导致权限校验错误,页面被跳转到 /nopower,若 NoPower 组件加载异常则会显示空白。
3. 路由守卫写法不符合 react-router-dom v6 规范
直接在 Route 的 element 属性中调用函数返回组件,会导致渲染时序问题,刷新时可能因状态未初始化完成引发异常。
具体修复步骤
步骤1:修复 Menu 组件的 location 属性
在 BasicLayout 中,已经通过 useLocation 获取了 location 对象,直接将其传入 Menu 组件即可,无需使用 props.location:
// BasicLayout.js const location = useLocation(); // ... <Menu data={props.userinfo.menu.rows} collapsed={collapsed} location={location} // 替换 props.location 为 location history={history} />
步骤2:修正权限校验逻辑
修改 checkRouterPower 函数,改为校验完整路径或父路径匹配,避免拆分路径导致的错误:
const checkRouterPower = (pathname) => { let menus; // 优先从 redux 取菜单,否则从 localStorage 读取 if (props.userinfo?.menu?.rows?.length) { menus = props.userinfo.menu.rows; } else { const localUser = localStorage.getItem("userinfo"); if (!localUser) return false; menus = JSON.parse(localUser).menu.rows; } if (!menus?.length) return false; // 提取所有有权限的路径(去掉开头的 /) const allowedPaths = menus.map(item => item.url.replace(/^\//, "")); // 当前路径去掉开头的 / const currentPath = pathname.replace(/^\//, ""); // 校验:当前路径完全匹配权限路径,或当前路径是权限路径的子路径 return allowedPaths.some(allowedPath => { return currentPath === allowedPath || currentPath.startsWith(`${allowedPath}/`); }); };
步骤3:调整路由守卫写法(符合 v6 规范)
将权限校验逻辑封装为独立的路由组件,避免直接在 element 中执行函数:
// BasicLayout.js 中新增 PowerRoute 组件 const PowerRoute = ({ element }) => { const location = useLocation(); const hasPower = checkRouterPower(location.pathname); if (!hasPower) { return <Navigate to="/nopower" replace />; } return element; }; // 然后修改路由配置: <Routes> <Route path="/" element={<Navigate to="/home" />}/> <Route path="/home" element={<PowerRoute element={<Home />} />}/> <Route path="/analytic/alertNdr" element={<PowerRoute element={<AlertNdr />} />}/> <Route path="/system/menuadmin" element={<PowerRoute element={<MenuAdmin />} />}/> <Route path="/system/poweradmin" element={<PowerRoute element={<PowerAdmin />} />}/> <Route path="/system/roleadmin" element={<PowerRoute element={<RoleAdmin />} />} /> <Route path="/nopower" element={<NoPower/>} /> <Route path="*" element={<NotFound/>} /> </Routes>
步骤4:修复 RouterContainer 中的路由配置
react-router-dom v6 的 BrowserRouter 不支持传入 history 属性,改为使用 Router 组件配合自定义 history:
// index.js import { Router, Routes, Route, Navigate, Outlet } from 'react-router-dom'; // 新增 PrivateRoute 组件处理登录校验 const PrivateRoute = () => { const userinfo = localStorage.getItem("userinfo"); if (!userinfo) { return <Navigate to="/public/login" replace />; } return <Outlet />; }; // 修改 RouterContainer 的返回部分: return ( <Router history={history}> <Routes> <Route path="/public/*" element={<UserLayout />} /> {/* 使用嵌套路由实现登录守卫 */} <Route path="/*" element={<PrivateRoute />}> <Route path="/*" element={<BasicLayout />} /> </Route> </Routes> </Router> );
步骤5:优化组件加载(可选,解决懒加载空白问题)
将 react-loadable 替换为 React 原生的 lazy 和 Suspense,确保组件加载时显示占位内容:
// BasicLayout.js import React, { Suspense, useState } from "react"; // 替换 Loadable 为 React.lazy const NotFound = React.lazy(() => import(`../views/error/404`)); const NoPower = React.lazy(() => import(`../views/error/401`)); const Home = React.lazy(() => import(`../views/Home`)); const AlertNdr = React.lazy(() => import(`../views/analytic/AlertNdr`)); const MenuAdmin = React.lazy(() => import(`../views/sys/MenuAdmin`)); const PowerAdmin = React.lazy(() => import(`../views/sys/PowerAdmin`)); const RoleAdmin = React.lazy(() => import(`../views/sys/RoleAdmin`)); const UserAdmin = React.lazy(() => import(`../views/sys/UserAdmin`)); // 定义 Loading 组件 const Loading = () => <div>加载中...</div>; // 用 Suspense 包裹路由内容 <Content className="content"> <Suspense fallback={<Loading />}> <Routes> {/* ...路由配置 */} </Routes> </Suspense> </Content>
内容的提问来源于stack exchange,提问作者chelsey lee

