umi动态路由高级用法:参数传递与路由守卫实现

umi动态路由高级用法:参数传递与路由守卫实现

【免费下载链接】umi A framework in react community ✨ 【免费下载链接】umi 项目地址: https://gitcode.com/gh_mirrors/umi8/umi

在React单页应用(SPA)开发中,路由系统是连接不同页面的桥梁。umi作为React社区的优秀框架,提供了强大的路由解决方案。本文将深入探讨umi动态路由的高级用法,包括参数传递技巧和路由守卫实现,帮助开发者构建更灵活、安全的路由系统。

动态路由基础

umi采用基于文件系统的路由约定,通过特殊命名的文件或目录实现动态路由功能。动态路由允许我们在URL中包含可变参数,如用户ID、文章编号等,从而实现页面的动态渲染。

动态路由定义方式

在umi中,有两种主要方式定义动态路由:

  1. 目录级动态路由:创建以$开头的目录,如$post,会生成形如/:post的路由
  2. 文件级动态路由:创建以$开头的文件,如$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应用提供了灵活而强大的路由解决方案。通过本文介绍的参数传递技巧和路由守卫实现方式,开发者可以构建出既灵活又安全的路由系统。

最佳实践总结

  1. 参数传递选择

    • 核心标识参数(如用户ID)使用URL路径参数
    • 可选参数或筛选条件使用查询字符串
    • 临时状态或敏感信息使用state参数
  2. 路由守卫策略

    • 页面级权限使用高阶组件或配置式路由守卫
    • 应用级权限使用全局Layout守卫
    • 复杂权限逻辑考虑使用专门的权限管理库
  3. 性能优化

    • 监听路由参数变化而非卸载/挂载组件
    • 使用动态导入(Dynamic Import)实现路由懒加载
    • 合理设计路由结构,避免过深嵌套

通过合理运用这些高级特性,你可以充分发挥umi路由系统的潜力,为用户提供流畅的导航体验,同时确保应用的安全性和可维护性。

掌握umi动态路由不仅能提升开发效率,还能为构建复杂单页应用提供坚实基础。建议深入阅读官方路由文档:docs/guide/router.md,探索更多高级功能。

希望本文对你的umi项目开发有所帮助!如果有任何问题或建议,欢迎在评论区留言讨论。

【免费下载链接】umi A framework in react community ✨ 【免费下载链接】umi 项目地址: https://gitcode.com/gh_mirrors/umi8/umi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值