Vue 开发者的 React 实战指南:路由和导航篇

作为 Vue 开发者,在迁移到 React 开发时,路由系统的差异是需要重点关注的部分。本文将从 Vue Router 的使用经验出发,详细介绍 React Router 的使用方式和最佳实践。

基础路由配置

Vue Router 配置

在 Vue 中,我们通常这样配置路由:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      component: About
    },
    {
      path: '/user/:id',
      name: 'user',
      component: () => import('../views/User.vue')
    }
  ]
});

export default router;

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App);
app.use(router);
app.mount('#app');

React Router 配置

在 React 中,我们使用 React Router:

// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import User from './pages/User';
import Layout from './components/Layout';

function App() {
  return (
    
      
        }>
          } />
          } />
          } />
          } />
        
      
    
  );
}

// Layout.jsx
import { Outlet } from 'react-router-dom';

function Layout() {
  return (
    
{/* 页脚 */}
); }

主要区别:

  1. 路由配置方式

    • Vue Router 使用配置对象
    • React Router 使用 JSX 声明式配置
  2. 路由嵌套方式

    • Vue Router 使用 children 配置
    • React Router 使用 Route 组件嵌套
  3. 布局组件实现

    • Vue Router 使用嵌套路由
    • React Router 使用 Outlet 组件

导航与参数获取

Vue Router 导航



React Router 导航

import { Link, useNavigate } from 'react-router-dom';

function Navigation() {
  const navigate = useNavigate();
  
  const handleNavigate = () => {
    navigate('/about');
    // 或者使用相对路径
    navigate('../about');
    // 带状态的导航
    navigate('/user/123', { state: { from: 'home' } });
  };
  
  return (
    
  );
}

参数获取对比

Vue Router:

React Router:

import { useParams, useSearchParams, useLocation } from 'react-router-dom';

function UserPage() {
  const { id } = useParams();
  const [searchParams] = useSearchParams();
  const location = useLocation();
  
  useEffect(() => {
    // 参数变化时的处理
    fetchUserData(id);
  }, [id]);
  
  return (
    

用户 ID: {id}

搜索词: {searchParams.get('search')}

来源: {location.state?.from}

); }

路由守卫

Vue Router 的导航守卫

// 全局前置守卫
router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login');
  } else {
    next();
  }
});

// 路由独享守卫
{
  path: '/admin',
  component: Admin,
  beforeEnter: (to, from, next) => {
    if (isAdmin()) {
      next();
    } else {
      next('/403');
    }
  }
}

// 组件内守卫
export default {
  beforeRouteEnter(to, from, next) {
    next(vm => {
      // 通过 vm 访问组件实例
    });
  }
}

React Router 的路由保护

// 路由保护组件
function PrivateRoute({ children }) {
  const auth = useAuth(); // 自定义 hook 获取认证状态
  const location = useLocation();
  
  if (!auth.isAuthenticated) {
    return ;
  }
  
  return children;
}

// 使用路由保护
function App() {
  return (
    
      
        }>
          } />
          
                
              
            }
          />
        
      
    
  );
}

// 自定义 Hook 实现路由守卫逻辑
function useRouteGuard(checkFn) {
  const location = useLocation();
  const navigate = useNavigate();
  
  useEffect(() => {
    if (!checkFn()) {
      navigate('/login', {
        state: { from: location },
        replace: true
      });
    }
  }, [location, navigate, checkFn]);
}

// 在组件中使用
function AdminPage() {
  useRouteGuard(() => isAdmin());
  
  return 
管理员页面
; }

实战示例:后台管理系统路由

让我们通过一个后台管理系统的路由配置来实践这些概念:

// types.ts
interface RouteConfig {
  path: string;
  element: React.ReactNode;
  children?: RouteConfig[];
  meta?: {
    title: string;
    icon?: string;
    requiresAuth?: boolean;
    permissions?: string[];
  };
}

// routes/index.tsx
import { lazy } from 'react';
import { Navigate } from 'react-router-dom';
import Layout from '@/components/Layout';

const Dashboard = lazy(() => import('@/pages/Dashboard'));
const UserList = lazy(() => import('@/pages/UserList'));
const UserDetail = lazy(() => import('@/pages/UserDetail'));
const Settings = lazy(() => import('@/pages/Settings'));

export const routes: RouteConfig[] = [
  {
    path: '/',
    element: ,
    children: [
      {
        path: '',
        element: 
      },
      {
        path: 'dashboard',
        element: ,
        meta: {
          title: '仪表盘',
          icon: 'dashboard',
          requiresAuth: true
        }
      },
      {
        path: 'users',
        meta: {
          title: '用户管理',
          icon: 'users',
          requiresAuth: true,
          permissions: ['user:read']
        },
        children: [
          {
            path: '',
            element: ,
            meta: {
              title: '用户列表'
            }
          },
          {
            path: ':id',
            element: ,
            meta: {
              title: '用户详情',
              permissions: ['user:read']
            }
          }
        ]
      },
      {
        path: 'settings',
        element: ,
        meta: {
          title: '系统设置',
          icon: 'settings',
          requiresAuth: true,
          permissions: ['settings:manage']
        }
      }
    ]
  },
  {
    path: '/login',
    element: ,
    meta: {
      title: '登录'
    }
  },
  {
    path: '*',
    element: ,
    meta: {
      title: '404'
    }
  }
];

// components/AuthRoute.tsx
function AuthRoute({ meta, children }: { meta?: RouteMeta; children: React.ReactNode }) {
  const { isAuthenticated, hasPermission } = useAuth();
  const location = useLocation();
  
  if (meta?.requiresAuth && !isAuthenticated) {
    return ;
  }
  
  if (meta?.permissions && !meta.permissions.every(hasPermission)) {
    return ;
  }
  
  return <>{children};
}

// App.tsx
function App() {
  return (
    
      }>
        
          {renderRoutes(routes)}
        
      
    
  );
}

// utils/route-helpers.tsx
function renderRoutes(routes: RouteConfig[]): React.ReactNode {
  return routes.map(route => (
    
          {route.element}
        
      }
    >
      {route.children && renderRoutes(route.children)}
    
  ));
}

// hooks/useTitle.ts
function useTitle(title?: string) {
  useEffect(() => {
    if (title) {
      document.title = `${title} - 管理系统`;
    }
  }, [title]);
}

// components/Layout.tsx
function Layout() {
  const location = useLocation();
  const { title } = useRouteMatch(routes)?.meta || {};
  
  useTitle(title);
  
  return (
    
); } // components/Sidebar.tsx function Sidebar({ routes }: { routes: RouteConfig[] }) { const location = useLocation(); const renderMenuItem = (route: RouteConfig) => { if (route.meta?.icon) { return ( }> {route.meta.title} ); } return null; }; const renderSubMenu = (route: RouteConfig) => { if (route.children?.length) { return ( } title={route.meta?.title} > {route.children.map(child => ( route.children?.length ? renderSubMenu(child) : renderMenuItem(child) ))} ); } return renderMenuItem(route); }; return ( {routes[0].children?.map(route => renderSubMenu(route))} ); }

性能优化

  1. 路由懒加载

    const UserList = lazy(() => import('./pages/UserList'));
    
    function App() {
      return (
     }>
       
         } />
       
     
      );
    }
  2. 预加载路由

    const UserList = lazy(() => import('./pages/UserList'));
    
    // 在合适的时机预加载
    const prefetchUserList = () => {
      const component = import('./pages/UserList');
    };
    
    // 例如在鼠标悬停在链接上时
    
      用户列表
    
  3. 路由缓存

    function CacheRoute({ cacheKey, element }) {
      const [cached, setCached] = useState(null);
      const location = useLocation();
      
      useEffect(() => {
     if (location.pathname === cacheKey) {
       setCached(element);
     }
      }, [location, cacheKey, element]);
      
      return cached || element;
    }

最佳实践

  1. 路由组织

    • 按功能模块组织路由
    • 使用懒加载优化性能
    • 合理使用路由元信息
    • 统一的路由配置管理
  2. 权限控制

    • 路由级别的权限控制
    • 细粒度的操作权限控制
    • 动态路由生成
    • 权限缓存优化
  3. 用户体验

    • 合理的加载状态
    • 平滑的过渡动画
    • 直观的导航提示
    • 友好的错误处理

小结

  1. React Router 的特点:

    • 声明式路由配置
    • 组件化的路由管理
    • 强大的 Hooks API
    • 灵活的路由保护
  2. 从 Vue Router 到 React Router 的转变:

    • 配置方式的转变
    • 导航守卫的实现
    • 参数获取的方式
    • 布局组织的差异
  3. 开发建议:

    • 深入理解路由原理
    • 掌握最佳实践
    • 注重性能优化
    • 关注用户体验

下一篇文章,我们将深入探讨 React 的表单处理和验证,帮助你构建更好的用户交互体验。

如果觉得这篇文章对你有帮助,别忘了点个赞

你可能感兴趣的:(Vue 开发者的 React 实战指南:路由和导航篇)