从零打造 AI 全栈应用(二):前端路由工程化设计与性能优化实践

7 阅读5分钟

在上一篇文章中
👉 《从零打造 AI 全栈应用(一):深度解析 Shadcn UI + Vite + NestJS 的工程化最佳实践》
我们从整体视角拆解了 AI 全栈项目的技术选型与工程结构,重点放在 UI 体系、构建工具与后端框架的协同设计 上。

但真正开始落地一个 AI 应用时,你会很快发现一个问题:

页面越来越多,能力越来越复杂,路由如果一开始没设计好,后期几乎一定要推倒重来。

因此,本文将单独聚焦一个经常被低估、却极其关键的模块 ——
前端路由的工程化设计


一、为什么 AI 应用对“路由设计”要求更高

与普通信息型网站不同,AI 应用通常具备以下特征:

  • 页面模块多(Chat / 订单 / 订阅 / 用户中心 / 历史记录)
  • 页面体积大(富交互、状态复杂)
  • 强登录态与权限边界
  • 高频异步加载(模型调用、流式返回)

如果路由层只是“能跳就行”,常见后果包括:

  • 首屏 JS 包过大,加载慢
  • 每次加页面都要改一堆路由逻辑
  • 登录态判断散落在各个页面
  • 布局代码重复、难以维护

👉 结论很明确:路由必须从一开始就工程化设计。


二、路由级懒加载:AI 应用的性能生命线

1. 问题本质:不是所有用户都会访问所有页面

在 AI 应用中:

  • 未登录用户只会访问登录页
  • 大部分用户高频使用的只有 Chat
  • 订单、个人中心访问频率远低于首页

如果一次性加载所有页面代码,本质是在 浪费用户带宽和首屏时间


2. React 中的路由级懒加载方案

React 官方提供了两块基础能力:

  • React.lazy:定义异步组件
  • Suspense:处理异步加载期间的 UI 状态

页面组件的声明方式如下:

import { lazy } from 'react';

const Home = lazy(() => import('@/pages/Home'));
const Mine = lazy(() => import('@/pages/Mine'));
const Login = lazy(() => import('@/pages/Login'));
const Chat = lazy(() => import('@/pages/Chat'));
const Order = lazy(() => import('@/pages/Order'));

这段代码背后做了三件非常重要的事:

  1. 每个页面会被打包成独立的 chunk
  2. 只有在路由命中时才会下载对应代码
  3. 极大降低首屏资源体积

📌 面试高频点
React.lazy 返回的是一个异步组件,必须运行在 Suspense 之下。


三、Suspense + Loading:把“等待”当成一等公民

<Suspense fallback={<Loading />}>
  <Routes>
    {/* 路由表 */}
  </Routes>
</Suspense>

1. Suspense 在这里到底解决什么问题

当用户首次进入某个路由时:

  • 对应页面 chunk 尚未下载完成
  • React 会暂停渲染该组件
  • Suspense 统一兜底展示 fallback

如果没有 Suspense:

  • ❌ 页面直接报错
  • ❌ 用户看到空白页面

2. 为什么 Loading 一定要抽成组件

在 AI 应用中,“加载中”并不是一个可忽略的瞬间状态:

  • 模型响应有真实等待时间
  • 页面切换频繁
  • 用户对流畅度非常敏感

将 Loading 抽成独立组件,可以:

  • 统一加载态视觉风格
  • 后期无痛升级为骨架屏
  • 区分首屏 Loading / 路由切换 Loading

👉 这不是 UI 细节,而是产品体验的一部分。


四、Layout Route:决定项目能走多远的设计

<Route path="/" element={<MainLayout />}>
  <Route path="" element={<Home />} />
  <Route path="mine" element={<Mine />} />
  <Route path="chat" element={<Chat />} />
  <Route path="order" element={<Order />} />
</Route>

这层结构是整个路由设计中最关键的一步


1. Layout Route 的核心思想

在 SPA 中,有两类组件:

  • 结构组件(Layout)
  • 内容组件(Page)

MainLayout 通常包含:

  • Header / 导航栏
  • 公共容器
  • <Outlet /> 子路由出口
function MainLayout() {
  return (
    <>
      <Header />
      <Outlet />
      <TabBar />
    </>
  );
}

当访问 /chat 时:

  • React Router 先渲染 MainLayout
  • 再将 Chat 渲染到 Outlet

👉 页面在变,结构不变。


2. 为什么 AI 应用特别适合这种设计

  • Chat、订单、个人中心共享布局
  • 新增模块只需添加子路由
  • 公共逻辑集中,维护成本低

这是一种 长期友好型架构


五、路由守卫:登录态与访问边界控制

AI 应用几乎一定存在明确的访问边界:

  • 未登录用户只能访问登录页
  • 登录后才能使用核心能力

但 React Router 并没有 Vue 那样的“路由守卫 API”。


1. React Router 的官方设计哲学

一切皆组件

因此,所谓“路由守卫”,本质就是 条件渲染


2. AuthRoute:最清晰、可维护的实现方式

import { Navigate } from 'react-router-dom';

function AuthRoute({ children }: { children: JSX.Element }) {
  const { isLogin } = useUserStore();

  if (!isLogin) {
    return <Navigate to="/login" replace />;
  }

  return children;
}

使用方式:

<Route
  path="order"
  element={
    <AuthRoute>
      <Order />
    </AuthRoute>
  }
/>

这种设计的优势非常明显:

  • 权限逻辑集中
  • 路由配置依然清晰
  • 与 Zustand / Redux 等状态管理天然契合

📌 面试一句话总结
React Router 的路由守卫,本质是通过组件包裹实现的条件控制。


六、RouterConfig:它是基础设施,不是业务代码

export default function RouterConfig({ children }) {
  return (
    <Router>
      <Suspense fallback={<Loading />}>
        <Routes>{/* 路由表 */}</Routes>
      </Suspense>
      {children}
    </Router>
  );
}

这个结构体现了一种非常成熟的工程意识:

  • Router 是最外层基础设施
  • Suspense 统一管理异步边界
  • children 为全局能力预留扩展点(如 Toast、全局弹窗、埋点)

👉 这已经不是“会用路由”,而是在“设计路由系统”。


七、总结:一个合格的 AI 应用路由层应该具备什么

  • 路由级懒加载(性能)
  • Suspense 统一兜底(稳定性)
  • Loading 抽象(体验)
  • Layout Route(结构清晰)
  • 路由守卫(业务边界)

如果你能把这套设计讲清楚:

面试官听到的不是 API,而是你的架构能力。