You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合Ant Design Layout管理路由?解决登录页与子路由异常

路由结构优化方案及认证集成指导

一、核心问题分析

  • 访问/produk报错的原因是根路由/设置了exact,导致只有严格匹配/时才会渲染布局和内容,子路由无法匹配到父路由,进而找不到对应路由。
  • 当前嵌套路由的写法不符合React Router v6规范,应使用Outlet实现布局嵌套。

二、路由结构重构步骤

1. 重构主路由结构

移除根路由的exact属性,将带侧边栏的布局封装为独立组件,用Outlet替代原AppContent渲染子路由内容,同时将原AppContent内的子路由移至主路由的children中:

import { Routes, Route, Outlet, Navigate } from 'react-router-dom';
import { Layout, Sider, Content, Footer } from 'antd';
import LoginPage from './LoginPage';
import Dashboard from './Dashboard';
import KalkulasiProduk from './KalkulasiProduk';
import Produk from './Produk';
import Supplier from './Supplier';
import LokasiKargo from './LokasiKargo';
import Aktivitas from './Aktivitas';
import Settings from './Settings';
import AppAlert from './AppAlert';
import SideNav from './SideNav';
import logo from './logo.png';
import style from './styles.module.css';

// 带侧边栏的主布局组件
const MainLayout = () => {
  return (
    <>
      <AppAlert />
      <Layout style={{ minHeight: "100vh" }}>
        <Sider>
          <div
            className={style.logo_wrapper}
            style={{ height: 32, margin: 16 }}
          >
            <img src={logo} alt="logo" />
            <p>Shipping Calculation</p>
          </div>
          <SideNav />
        </Sider>
        <Layout className="site-layout">
          <Content style={{ margin: "0 16px" }}>
            <div style={{ marginTop: "16px", padding: 24, minHeight: 360 }}>
              {/* 用Outlet渲染子路由内容 */}
              <Outlet />
            </div>
          </Content>
          <Footer style={{ textAlign: "center" }}>
            Shipping Calculation ©2023 Created by Owenn Gimli
          </Footer>
        </Layout>
      </Layout>
    </>
  );
};

const AppRoutes = () => {
  return (
    <div>
      <Routes>
        {/* 无需侧边栏的登录页 */}
        <Route path="/login" element={<LoginPage />} />
        
        {/* 带侧边栏的主布局路由,包含所有子路由 */}
        <Route path="/" element={<MainLayout />}>
          <Route index element={<Dashboard />} /> {/* 根路径默认渲染Dashboard */}
          <Route path="kalkulasi-produk" element={<KalkulasiProduk />} />
          <Route path="produk" element={<Produk />} />
          <Route path="supplier" element={<Supplier />} />
          <Route path="lokasi-kargo" element={<LokasiKargo />} />
          <Route path="aktivitas" element={<Aktivitas />} />
          <Route path="settings" element={<Settings />} />
        </Route>

        {/* 404路由,处理未定义路径 */}
        <Route path="*" element={<Navigate to="/" replace />} />
      </Routes>
    </div>
  );
};

export default AppRoutes;

2. 集成Firebase认证的私有路由

创建私有路由组件,保护需要登录才能访问的主布局路由:

import { useEffect, useState } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { getAuth, onAuthStateChanged } from 'firebase/auth';

const PrivateRoute = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(null);
  const auth = getAuth();

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setIsAuthenticated(!!user);
    });

    return () => unsubscribe();
  }, [auth]);

  // 加载状态可替换为自定义loading组件
  if (isAuthenticated === null) {
    return <div>Loading...</div>;
  }

  return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
};

修改主路由,用私有路由包裹主布局:

const AppRoutes = () => {
  return (
    <div>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        
        {/* 用私有路由保护主布局下的所有子路由 */}
        <Route element={<PrivateRoute />}>
          <Route path="/" element={<MainLayout />}>
            <Route index element={<Dashboard />} />
            <Route path="kalkulasi-produk" element={<KalkulasiProduk />} />
            <Route path="produk" element={<Produk />} />
            <Route path="supplier" element={<Supplier />} />
            <Route path="lokasi-kargo" element={<LokasiKargo />} />
            <Route path="aktivitas" element={<Aktivitas />} />
            <Route path="settings" element={<Settings />} />
          </Route>
        </Route>

        <Route path="*" element={<Navigate to="/" replace />} />
      </Routes>
    </div>
  );
};

3. 登录页跳转逻辑

在LoginPage中实现登录成功后的页面跳转:

import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
import { useNavigate } from 'react-router-dom';
import { Form, Input, Button } from 'antd';

const LoginPage = () => {
  const navigate = useNavigate();
  const auth = getAuth();

  const handleLogin = async (values) => {
    try {
      await signInWithEmailAndPassword(auth, values.email, values.password);
      // 登录成功跳转到首页
      navigate('/', { replace: true });
    } catch (error) {
      console.error('登录失败:', error);
      // 可添加错误提示组件
    }
  };

  return (
    <div style={{ maxWidth: 300, margin: '100px auto' }}>
      <h2>登录</h2>
      <Form onFinish={handleLogin}>
        <Form.Item
          name="email"
          rules={[{ required: true, message: '请输入邮箱' }]}
        >
          <Input placeholder="邮箱" />
        </Form.Item>
        <Form.Item
          name="password"
          rules={[{ required: true, message: '请输入密码' }]}
        >
          <Input.Password placeholder="密码" />
        </Form.Item>
        <Form.Item>
          <Button type="primary" htmlType="submit" block>登录</Button>
        </Form.Item>
      </Form>
    </div>
  );
};

export default LoginPage;

三、关键说明

  • 移除根路由exact属性:React Router v6中父路由默认匹配所有以其路径开头的子路由,无需手动设置exact。
  • 使用Outlet实现嵌套:这是React Router v6推荐的布局嵌套写法,替代传统子路由渲染方式。
  • 私有路由作用:确保未登录用户无法直接访问主布局页面,自动跳转至登录页。
  • 404路由:处理所有未定义路径,避免出现路由匹配错误。

内容的提问来源于stack exchange,提问作者Owenn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:01:28