umi动态路由高级用法:参数传递与路由守卫实现
【免费下载链接】umi A framework in react community ✨ 项目地址: https://gitcode.com/gh_mirrors/umi8/umi
在React单页应用(SPA)开发中,路由系统是连接不同页面的桥梁。umi作为React社区的优秀框架,提供了强大的路由解决方案。本文将深入探讨umi动态路由的高级用法,包括参数传递技巧和路由守卫实现,帮助开发者构建更灵活、安全的路由系统。
动态路由基础
umi采用基于文件系统的路由约定,通过特殊命名的文件或目录实现动态路由功能。动态路由允许我们在URL中包含可变参数,如用户ID、文章编号等,从而实现页面的动态渲染。
动态路由定义方式
在umi中,有两种主要方式定义动态路由:
- 目录级动态路由:创建以
$开头的目录,如$post,会生成形如/:post的路由 - 文件级动态路由:创建以
$开头的文件,如$id.js,会生成形如/:id的路由
官方文档中提供了详细的路由规则说明:docs/guide/router.md
基础示例
以下目录结构:
+ pages/
+ users/
$id.js
+ $post/
index.js
comments.js
会自动生成路由配置:
[
{ path: '/users/:id', exact: true, component: './pages/users/$id.js' },
{ path: '/:post', exact: true, component: './pages/$post/index.js' },
{ path: '/:post/comments', exact: true, component: './pages/$post/comments.js' }
]
参数传递与接收
动态路由的核心价值在于参数传递,umi提供了多种方式在路由间传递和接收参数,满足不同场景需求。
URL路径参数
这是最常见的参数传递方式,通过URL路径中的动态段传递参数。
传递参数
使用声明式导航传递参数:
import Link from 'umi/link';
// 在列表页中
<Link to={`/users/${user.id}`}>查看用户 {user.name}</Link>
或使用命令式导航:
import router from 'umi/router';
// 点击按钮跳转到用户详情页
const goToUserDetail = (userId) => {
router.push(`/users/${userId}`);
};
接收参数
在目标页面组件中,通过useParams钩子或withRouter高阶组件获取参数:
// pages/users/$id.js
import { useParams } from 'umi';
export default function UserProfile() {
// 使用useParams钩子获取参数
const { id } = useParams();
return <div>用户ID: {id}</div>;
}
查询字符串参数
对于非核心参数或可选参数,查询字符串是理想的传递方式,如分页信息、筛选条件等。
传递查询参数
// 声明式
<Link to={{ pathname: '/list', query: { page: 1, category: 'news' } }}>
新闻列表第一页
</Link>
// 命令式
router.push({
pathname: '/list',
query: { page: 1, category: 'news' }
});
接收查询参数
// pages/list.js
import { useLocation } from 'umi';
import { useEffect } from 'react';
export default function ListPage() {
const location = useLocation();
useEffect(() => {
// 获取查询参数
const { page, category } = location.query;
// 根据参数加载数据
loadData(page, category);
}, [location.query]);
return <div>列表页面</div>;
}
状态参数
某些场景下,我们需要传递参数但不想在URL中显示,这时可以使用状态参数(state)。
传递状态参数
// 声明式
<Link
to={{
pathname: '/detail',
state: { fromList: true, scrollPosition: 100 }
}}
>
详情页
</Link>
// 命令式
router.push({
pathname: '/detail',
state: { fromList: true, scrollPosition: 100 }
});
接收状态参数
// pages/detail.js
import { useLocation } from 'umi';
export default function DetailPage() {
const location = useLocation();
// 获取状态参数
const { fromList, scrollPosition } = location.state || {};
return <div>详情页面</div>;
}
路由守卫实现
路由守卫(Route Guard)是控制页面访问权限的重要机制,可用于实现登录验证、角色权限检查、数据预加载等功能。
基于配置的路由守卫
umi支持在路由配置中指定自定义Route组件,实现路由级别的权限控制。
1. 创建权限路由组件
首先,创建一个权限路由组件:
// src/routes/PrivateRoute.js
import { Route, Redirect } from 'react-router-dom';
import { useAuth } from '../utils/auth';
export default function PrivateRoute({ component: Component, ...rest }) {
const { isAuthenticated, userRole } = useAuth();
return (
<Route
{...rest}
render={props => {
// 未登录用户重定向到登录页
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
// 角色权限检查
if (rest.requiredRole && userRole !== rest.requiredRole) {
return <Redirect to="/forbidden" />;
}
// 有权限,渲染目标组件
return <Component {...props} />;
}}
/>
);
}
2. 配置路由守卫
在.umirc.js中配置需要保护的路由:
// .umirc.js
export default {
pages: {
'/profile': { Route: './src/routes/PrivateRoute.js' },
'/admin': {
Route: './src/routes/PrivateRoute.js',
requiredRole: 'admin' // 自定义属性,会传递给PrivateRoute
},
'/users/:id': { Route: './src/routes/PrivateRoute.js' }
}
};
官方文档中提供了更详细的权限路由配置说明:docs/guide/router.md
基于高阶组件的路由守卫
对于需要在多个组件中复用的权限逻辑,可以创建高阶组件。
// src/hocs/withAuth.js
import { useEffect } from 'react';
import { useHistory } from 'umi';
import { useAuth } from '../utils/auth';
export default function withAuth(Component, requiredRole = null) {
return function AuthProtectedComponent(props) {
const { isAuthenticated, userRole, loading } = useAuth();
const history = useHistory();
useEffect(() => {
// 权限检查逻辑
if (!loading) {
if (!isAuthenticated) {
history.push('/login');
} else if (requiredRole && userRole !== requiredRole) {
history.push('/forbidden');
}
}
}, [isAuthenticated, loading]);
// 加载中或无权限时不渲染组件
if (loading || !isAuthenticated || (requiredRole && userRole !== requiredRole)) {
return <div>加载中...</div>;
}
// 有权限,渲染组件
return <Component {...props} />;
};
}
使用方式:
// pages/admin/dashboard.js
import withAuth from '../../hocs/withAuth';
function AdminDashboard() {
return <div>管理员控制台</div>;
}
// 仅允许admin角色访问
export default withAuth(AdminDashboard, 'admin');
基于全局Layout的路由守卫
对于应用级别的路由控制,可以在全局Layout中实现:
// src/layouts/index.js
import { useEffect } from 'react';
import { useLocation, useHistory } from 'umi';
import { useAuth } from '../utils/auth';
export default function GlobalLayout({ children }) {
const location = useLocation();
const history = useHistory();
const { isAuthenticated } = useAuth();
// 需要登录的路由列表
const authRequiredPaths = ['/profile', '/admin', '/settings'];
useEffect(() => {
const isAuthPath = authRequiredPaths.some(path =>
location.pathname.startsWith(path)
);
// 如果访问需要登录的页面且未登录,重定向到登录页
if (isAuthPath && !isAuthenticated) {
history.push({
pathname: '/login',
state: { from: location.pathname } // 记录来源路径,登录后可跳转回来
});
}
}, [location.pathname, isAuthenticated]);
return (
<div className="app-container">
<Header />
<main>{children}</main>
<Footer />
</div>
);
}
高级应用场景
动态路由与嵌套路由结合
结合动态路由和嵌套路由可以创建复杂的页面结构,如:
+ pages/
+ products/
- _layout.js
- index.js
- $id.js
+ $id/
- comments.js
- details.js
生成的路由配置:
[
{
path: '/products',
component: './pages/products/_layout.js',
routes: [
{ path: '/products', exact: true, component: './pages/products/index.js' },
{ path: '/products/:id', exact: true, component: './pages/products/$id.js' },
{ path: '/products/:id/comments', exact: true, component: './pages/products/$id/comments.js' },
{ path: '/products/:id/details', exact: true, component: './pages/products/$id/details.js' }
]
}
]
路由参数变化监听
有时需要在路由参数变化时重新加载数据,而不是卸载/挂载组件:
// pages/products/$id.js
import { useParams, useRouteMatch } from 'umi';
import { useEffect, useState } from 'react';
import { fetchProduct } from '../../services/api';
export default function ProductDetail() {
const { id } = useParams();
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
// 当id变化时重新获取数据
useEffect(() => {
const loadProduct = async () => {
setLoading(true);
try {
const data = await fetchProduct(id);
setProduct(data);
} catch (error) {
console.error('加载产品失败:', error);
} finally {
setLoading(false);
}
};
loadProduct();
}, [id]); // 仅在id变化时执行
if (loading) return <div>加载中...</div>;
if (!product) return <div>产品不存在</div>;
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
</div>
);
}
总结与最佳实践
umi的动态路由系统为React应用提供了灵活而强大的路由解决方案。通过本文介绍的参数传递技巧和路由守卫实现方式,开发者可以构建出既灵活又安全的路由系统。
最佳实践总结
-
参数传递选择:
- 核心标识参数(如用户ID)使用URL路径参数
- 可选参数或筛选条件使用查询字符串
- 临时状态或敏感信息使用state参数
-
路由守卫策略:
- 页面级权限使用高阶组件或配置式路由守卫
- 应用级权限使用全局Layout守卫
- 复杂权限逻辑考虑使用专门的权限管理库
-
性能优化:
- 监听路由参数变化而非卸载/挂载组件
- 使用动态导入(Dynamic Import)实现路由懒加载
- 合理设计路由结构,避免过深嵌套
通过合理运用这些高级特性,你可以充分发挥umi路由系统的潜力,为用户提供流畅的导航体验,同时确保应用的安全性和可维护性。
掌握umi动态路由不仅能提升开发效率,还能为构建复杂单页应用提供坚实基础。建议深入阅读官方路由文档:docs/guide/router.md,探索更多高级功能。
希望本文对你的umi项目开发有所帮助!如果有任何问题或建议,欢迎在评论区留言讨论。
【免费下载链接】umi A framework in react community ✨ 项目地址: https://gitcode.com/gh_mirrors/umi8/umi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



