ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

企业级AI知识库前端架构实战:React+TS+Ant Design项目结构全解析

企业级AI知识库前端架构实战:React+TS+Ant Design项目结构全解析 如果你是一名前端开发者最近一定被各种“AI知识库”项目刷屏了。从Dify、FastGPT到各种开源方案似乎一夜之间给大模型“装个脑子”成了标配。但当你真正想动手把一个像“火山方舟”那样功能齐全的企业级知识库从零搭建起来时却很可能陷入迷茫前端到底要做什么后端那么多模块怎么对接整个项目的代码结构应该如何组织才清晰、可维护这不是一个简单的调用API的Demo。一个对标火山方舟的知识库系统意味着你需要处理复杂的多轮对话、流式响应、文件解析、向量检索、权限管理以及一个体验流畅的管理后台。前端不再只是写写静态页面而是需要深度理解整个AI应用的架构和数据流。本文将从前端工程师的视角彻底拆解一个企业级AI知识库的核心功能、技术分层和项目结构。我不会只讲概念而是会结合一个具体的、可落地的React TypeScript Ant Design前端项目结构示例告诉你每个目录该放什么组件如何划分状态如何管理以及如何与后端无论是Spring AI还是Python FastAPI优雅对接。读完本文你将能清晰地规划出属于你自己的知识库前端项目骨架避开初期混乱的坑直接进入高效开发。1. 为什么前端开发者需要关注知识库项目架构很多前端同学看到“AI知识库”第一反应是“这是后端和算法的事我调个接口不就行了” 这个想法会让你在项目中期陷入被动。一个成熟的知识库产品其复杂度的70%体现在交互逻辑和状态管理上而这正是前端的核心战场。核心痛点在于“状态爆炸”。对比一个传统的增删改查后台传统后台列表、表单、弹窗。状态无非是loading、data、pagination。AI知识库后台你需要同时管理对话列表、当前会话消息流、文件上传解析进度、知识库同步状态、模型切换、Token消耗统计、流式输出的实时渲染……这些状态相互交织且很多是异步、长时任务。如果你没有在项目初期就设计好清晰的数据流和分层代码很快就会变成“意大利面条”。更关键的是前端是用户体验的最后一道关口。后端的RAG检索再精准如果前端无法流畅地展示流式回答、无法优雅地处理文件上传和解析状态、无法提供清晰的知识库管理界面整个产品的价值就会大打折扣。因此本文的核心判断是前端开发者必须从“接口调用者”转变为“AI应用架构的参与者”。你需要从功能模块出发自上而下地设计前端项目结构确保技术方案能支撑复杂的产品需求并为未来的功能迭代留出空间。2. 对标火山方舟企业级知识库的核心功能模块拆解在动手写代码之前我们必须明确要构建什么。火山方舟、Dify等平台提供了一个功能范本。我们可以将其核心功能归纳为两大门户和六个核心模块。2.1 两大用户门户运营管理后台供管理员、运营人员使用。核心是知识库的“生产资料”管理。AI对话应用端供最终用户使用。核心是基于知识库的问答交互体验。2.2 六个核心前端功能模块围绕两大门户前端需要实现以下模块模块名称所属门户核心功能前端技术挑战1. 知识库管理管理后台知识库的CRUD、文档上传支持PDF、Word、TXT、Markdown、文档解析状态同步、文档分块/向量化策略配置、知识库检索测试。大文件分片上传、上传进度管理、长任务状态轮询、复杂表单配置。2. 对话与会话管理应用端/后台创建/删除对话会话、会话列表展示、跨会话切换、会话重命名、会话归档。多会话状态隔离、本地存储同步、列表实时更新。3. 智能对话界面应用端消息列表展示用户/AI、流式文本输出渲染、Markdown渲染、代码高亮、消息复制、重新生成、引用来源追溯。流式响应(SSE/WebSocket)处理、滚动定位、引用跳转、交互状态管理。4. 模型与参数配置管理后台/应用端多模型供应商OpenAI、通义、文心等配置、API密钥管理、模型切换、推理参数温度、top_p等调节。配置表单的验证与加密存储、全局模型状态管理。5. 权限与团队管理管理后台用户/角色管理、知识库访问权限分配、操作日志审计。复杂的树形权限组件、基于角色的UI元素权限控制。6. 数据统计与监控管理后台Token消耗统计、问答次数统计、知识库使用热度、系统状态监控。图表集成ECharts/Ant Design Charts、数据定时刷新。这六个模块构成了前端开发的主要工作范围。接下来我们将把这些功能映射到具体的项目文件和目录结构中。3. 技术选型与分层架构设计在开始创建文件夹之前我们需要确定技术栈和架构模式。清晰的分层能有效管理上述复杂功能带来的复杂度。基础技术栈推荐框架React 18 (Next.js可用于SSR但纯后台应用也可用Vite React)语言TypeScript (必备用于管理复杂的接口类型)UI组件库Ant Design (ProComponents更佳) 或 Arco Design状态管理Zustand / Redux Toolkit (推荐Zustand更轻量)请求库Axios (封装为统一的request工具)路由React-Router-DOM v6工具Day.js (日期)、Ahooks (React Hooks库)、Tailwind CSS (可选用于快速自定义样式)核心分层架构前端视角1. 视图层 (View/Components): 负责UI渲染和用户交互。 2. 状态层 (State/Store): 集中管理应用状态用户、会话、知识库列表等。 3. 服务层 (Services/API): 封装所有后端API请求定义数据接口。 4. 工具层 (Utils/Hooks): 提供通用功能请求封装、文件处理、流式处理Hook。这个分层将贯穿我们的项目结构设计。4. 项目结构深度拆解从目录到代码这是本文的核心。我们将创建一个名为ai-knowledge-base-frontend的项目并逐一解释每个目录和文件的作用。4.1 项目根目录结构ai-knowledge-base-frontend/ ├── public/ # 静态资源 ├── src/ # 源代码 │ ├── api/ # 【服务层】API请求封装 │ ├── assets/ # 静态资源图片、字体等 │ ├── components/ # 【视图层】通用业务组件 │ ├── config/ # 应用配置路由、菜单、常量 │ ├── hooks/ # 【工具层】自定义React Hooks │ ├── layouts/ # 布局组件 │ ├── pages/ # 页面组件对应路由 │ ├── stores/ # 【状态层】全局状态管理Zustand │ ├── types/ # TypeScript 类型定义 │ ├── utils/ # 【工具层】通用工具函数 │ ├── App.tsx # 应用根组件 │ ├── main.tsx # 应用入口 │ └── vite-env.d.ts # Vite类型声明 ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 ├── index.html # HTML入口 ├── package.json ├── tsconfig.json └── vite.config.ts # Vite配置4.2 核心目录与文件详解4.2.1src/api/- 服务层对接后端这里按业务模块组织API每个文件对应一个后端资源或功能。// src/api/knowledgeBase.ts import request from /utils/request; // 定义知识库相关的所有接口 export interface KnowledgeBaseItem { id: string; name: string; description?: string; documentCount: number; embeddingModel: string; createdAt: string; } export interface DocumentItem { id: string; name: string; size: number; status: processing | success | error; errorMsg?: string; } // 1. 获取知识库列表 export const fetchKnowledgeBaseList (params: { page: number; pageSize: number }) request.get{ list: KnowledgeBaseItem[]; total: number }(/api/kb/list, { params }); // 2. 创建知识库 export const createKnowledgeBase (data: { name: string; description?: string }) request.postKnowledgeBaseItem(/api/kb, data); // 3. 上传文档到指定知识库 (关键处理文件上传进度) export const uploadDocument ( kbId: string, file: File, onUploadProgress?: (progressEvent: any) void ) { const formData new FormData(); formData.append(file, file); return request.postDocumentItem(/api/kb/${kbId}/document, formData, { headers: { Content-Type: multipart/form-data }, onUploadProgress, // 传入进度回调函数 timeout: 60000, // 长超时 }); }; // 4. 查询知识库下的文档列表 export const fetchDocuments (kbId: string, params: { page: number; pageSize: number }) request.get{ list: DocumentItem[]; total: number }(/api/kb/${kbId}/documents, { params });// src/api/chat.ts import request from /utils/request; // 定义对话相关接口 export interface Message { id: string; role: user | assistant | system; content: string; createdAt: string; metadata?: { // 用于存储引用来源等信息 references?: Array{ doc_id: string; content: string }; }; } export interface ChatSession { id: string; title: string; // 通常由首条消息生成 knowledgeBaseId?: string; // 绑定的知识库 updatedAt: string; } // 1. 获取会话列表 export const fetchSessions () request.getChatSession[](/api/chat/sessions); // 2. 创建新会话 export const createSession (data: { title?: string; knowledgeBaseId?: string }) request.postChatSession(/api/chat/session, data); // 3. 发送消息流式与非流式 // 非流式一次性返回 export const sendMessage (sessionId: string, data: { content: string }) request.postMessage(/api/chat/message, { sessionId, ...data }); // 流式使用EventSource或ReadableStream这里以EventSource为例 // 注意这是一个返回EventSource实例的函数需要在组件中监听事件 export const sendMessageStream (sessionId: string, content: string) { const eventSource new EventSource( /api/chat/message/stream?sessionId${sessionId}content${encodeURIComponent(content)} ); return eventSource; };4.2.2src/stores/- 状态层管理全局数据使用Zustand创建Store管理会话、知识库等全局状态。// src/stores/useChatStore.ts import { create } from zustand; import { ChatSession, Message } from /api/chat; interface ChatState { // 当前选中的会话ID currentSessionId: string | null; // 会话列表 sessions: ChatSession[]; // 消息映射表sessionId - Message[] messagesMap: Recordstring, Message[]; // 加载状态 loading: boolean; // Actions setCurrentSession: (sessionId: string) void; setSessions: (sessions: ChatSession[]) void; addSession: (session: ChatSession) void; updateSessionTitle: (sessionId: string, title: string) void; setMessages: (sessionId: string, messages: Message[]) void; appendMessage: (sessionId: string, message: Message) void; updateLastMessage: (sessionId: string, content: string) void; // 用于流式更新 fetchSessions: () Promisevoid; } export const useChatStore createChatState((set, get) ({ currentSessionId: null, sessions: [], messagesMap: {}, loading: false, setCurrentSession: (sessionId) set({ currentSessionId: sessionId }), setSessions: (sessions) set({ sessions }), addSession: (session) set((state) ({ sessions: [session, ...state.sessions] })), updateSessionTitle: (sessionId, title) set((state) ({ sessions: state.sessions.map(s s.id sessionId ? { ...s, title } : s ), })), setMessages: (sessionId, messages) set((state) ({ messagesMap: { ...state.messagesMap, [sessionId]: messages }, })), appendMessage: (sessionId, message) set((state) { const existingMessages state.messagesMap[sessionId] || []; return { messagesMap: { ...state.messagesMap, [sessionId]: [...existingMessages, message] }, }; }), // 关键流式响应时持续更新最后一条AI消息的内容 updateLastMessage: (sessionId, content) set((state) { const messages state.messagesMap[sessionId]; if (!messages || messages.length 0) return state; const lastMessage messages[messages.length - 1]; if (lastMessage.role ! assistant) return state; const updatedMessages [...messages]; updatedMessages[updatedMessages.length - 1] { ...lastMessage, content: lastMessage.content content, }; return { messagesMap: { ...state.messagesMap, [sessionId]: updatedMessages }, }; }), // 异步Action获取会话列表 fetchSessions: async () { set({ loading: true }); try { const { fetchSessions } await import(/api/chat); const data await fetchSessions(); set({ sessions: data }); // 默认选中第一个会话 if (data.length 0 !get().currentSessionId) { set({ currentSessionId: data[0].id }); } } catch (error) { console.error(Failed to fetch sessions:, error); } finally { set({ loading: false }); } }, }));4.2.3src/hooks/- 工具层封装复杂逻辑自定义Hook是处理复杂交互如流式响应、文件上传的最佳实践。// src/hooks/useChatStream.ts import { useEffect, useRef, useCallback } from react; import { useChatStore } from /stores/useChatStore; import { sendMessageStream } from /api/chat; /** * 用于处理流式对话的Hook * param sessionId 当前会话ID * returns 发送消息的函数和状态 */ export const useChatStream (sessionId: string) { const { appendMessage, updateLastMessage } useChatStore(); const eventSourceRef useRefEventSource | null(null); // 清理函数 const cleanup useCallback(() { if (eventSourceRef.current) { eventSourceRef.current.close(); eventSourceRef.current null; } }, []); // 组件卸载时清理 useEffect(() { return cleanup; }, [cleanup]); const sendStreamMessage useCallback(async (content: string) { // 1. 立即添加用户消息到UI const userMessage { id: user_${Date.now()}, role: user as const, content, createdAt: new Date().toISOString(), }; appendMessage(sessionId, userMessage); // 2. 添加一个初始的AI消息占位符 const aiMessageId ai_${Date.now()}; const aiMessage { id: aiMessageId, role: assistant as const, content: , createdAt: new Date().toISOString(), }; appendMessage(sessionId, aiMessage); // 3. 建立流式连接 cleanup(); // 先清理之前的连接 const eventSource sendMessageStream(sessionId, content); eventSourceRef.current eventSource; eventSource.onmessage (event) { try { const data JSON.parse(event.data); if (data.type chunk) { // 持续更新最后一条AI消息的内容 updateLastMessage(sessionId, data.content); } else if (data.type end) { // 流式结束可以添加最终处理如保存到数据库 console.log(Stream finished:, data); cleanup(); } else if (data.type references) { // 处理引用来源可以更新消息的metadata console.log(References:, data.references); } } catch (error) { console.error(Parse stream error:, error); } }; eventSource.onerror (error) { console.error(EventSource failed:, error); // 可以更新AI消息为错误状态 updateLastMessage(sessionId, \n\n【网络连接异常请重试】); cleanup(); }; }, [sessionId, appendMessage, updateLastMessage, cleanup]); return { sendStreamMessage, cleanup }; };// src/hooks/useFileUpload.ts import { useState, useCallback } from react; import { message } from antd; import { uploadDocument } from /api/knowledgeBase; interface UploadState { progress: number; status: idle | uploading | success | error; error?: string; } /** * 封装文件上传逻辑的Hook * param kbId 知识库ID * returns 上传函数和状态 */ export const useFileUpload (kbId: string) { const [uploadState, setUploadState] useStateUploadState({ progress: 0, status: idle, }); const handleUpload useCallback(async (file: File) { // 基础验证 const allowedTypes [.pdf, .docx, .txt, .md, .csv]; const fileExt file.name.slice(file.name.lastIndexOf(.)).toLowerCase(); if (!allowedTypes.includes(fileExt)) { message.error(不支持的文件格式: ${fileExt}请上传 ${allowedTypes.join(, )} 格式文件); return; } if (file.size 100 * 1024 * 1024) { // 100MB限制 message.error(文件大小不能超过100MB); return; } setUploadState({ progress: 0, status: uploading }); try { const onUploadProgress (progressEvent: any) { const percent Math.round((progressEvent.loaded * 100) / progressEvent.total); setUploadState(prev ({ ...prev, progress: percent })); }; const result await uploadDocument(kbId, file, onUploadProgress); setUploadState({ progress: 100, status: success }); message.success(文件 ${file.name} 上传成功正在解析中...); return result; // 返回上传结果供调用方使用 } catch (error: any) { const errorMsg error.response?.data?.message || 上传失败请重试; setUploadState({ progress: 0, status: error, error: errorMsg }); message.error(errorMsg); throw error; } }, [kbId]); const resetUpload useCallback(() { setUploadState({ progress: 0, status: idle }); }, []); return { uploadState, handleUpload, resetUpload }; };4.2.4src/components/- 视图层构建可复用UI按功能划分组件注意通用性和业务组件的分离。src/components/ ├── common/ # 通用组件 │ ├── Loading.tsx │ ├── ErrorBoundary.tsx │ └── MarkdownRenderer.tsx # 渲染AI返回的Markdown ├── chat/ # 对话相关组件 │ ├── MessageList.tsx │ ├── MessageItem.tsx │ ├── ChatInput.tsx │ └── SessionSidebar.tsx ├── knowledge-base/ # 知识库相关组件 │ ├── KBCard.tsx │ ├── DocumentTable.tsx │ ├── UploadDragger.tsx │ └── ChunkConfigForm.tsx └── layout/ # 布局组件也可放在src/layouts/ └── MainLayout.tsx// src/components/chat/MessageItem.tsx import React from react; import { Avatar, Typography, Space } from antd; import { UserOutlined, RobotOutlined } from ant-design/icons; import { Message } from /api/chat; import MarkdownRenderer from /components/common/MarkdownRenderer; const { Text } Typography; interface MessageItemProps { message: Message; } const MessageItem: React.FCMessageItemProps ({ message }) { const isUser message.role user; return ( div style{{ display: flex, padding: 16px 0, backgroundColor: isUser ? #fafafa : transparent, }} {/* 头像 */} div style{{ marginRight: 12, flexShrink: 0 }} Avatar sizelarge icon{isUser ? UserOutlined / : RobotOutlined /} style{{ backgroundColor: isUser ? #1890ff : #52c41a }} / /div {/* 内容区域 */} div style{{ flex: 1, overflow: hidden }} Space directionvertical size{2} style{{ width: 100% }} Text strong{isUser ? You : AI Assistant}/Text div style{{ fontSize: 15px, lineHeight: 1.6 }} MarkdownRenderer content{message.content} / /div {/* 引用来源展示 */} {message.metadata?.references message.metadata.references.length 0 ( div style{{ marginTop: 8, fontSize: 12px, color: #666 }} Text typesecondary参考来源/Text {message.metadata.references.slice(0, 3).map((ref, idx) ( div key{idx} style{{ marginTop: 4 }} Text ellipsis• {ref.content.substring(0, 100)}.../Text /div ))} /div )} {/* 时间戳 */} Text typesecondary style{{ fontSize: 12px }} {new Date(message.createdAt).toLocaleTimeString()} /Text /Space /div /div ); }; export default MessageItem;// src/components/common/MarkdownRenderer.tsx import React from react; import ReactMarkdown from react-markdown; import { Prism as SyntaxHighlighter } from react-syntax-highlighter; import { vscDarkPlus } from react-syntax-highlighter/dist/esm/styles/prism; import remarkGfm from remark-gfm; // 支持表格、删除线等 interface MarkdownRendererProps { content: string; } const MarkdownRenderer: React.FCMarkdownRendererProps ({ content }) { return ( ReactMarkdown remarkPlugins{[remarkGfm]} components{{ code({ node, inline, className, children, ...props }) { const match /language-(\w)/.exec(className || ); return !inline match ? ( SyntaxHighlighter style{vscDarkPlus} language{match[1]} PreTagdiv {...props} {String(children).replace(/\n$/, )} /SyntaxHighlighter ) : ( code className{className} {...props} {children} /code ); }, // 可以继续自定义其他组件如a、img等 }} {content} /ReactMarkdown ); }; export default MarkdownRenderer;4.2.5src/pages/- 页面组件组合业务模块使用基于文件的路由如Next.js或集中式路由配置。src/pages/ ├── Dashboard.tsx # 仪表盘 ├── KnowledgeBase/ │ ├── index.tsx # 知识库列表页 │ └── [id].tsx # 知识库详情页文档管理 ├── Chat/ │ └── index.tsx # 主对话页面 ├── Settings/ │ ├── ModelConfig.tsx # 模型配置 │ └── TeamManagement.tsx # 团队管理 └── 404.tsx// src/pages/Chat/index.tsx - 对话主页面 import React, { useEffect, useState } from react; import { Layout, Spin, message } from antd; import { useChatStore } from /stores/useChatStore; import { useChatStream } from /hooks/useChatStream; import SessionSidebar from /components/chat/SessionSidebar; import MessageList from /components/chat/MessageList; import ChatInput from /components/chat/ChatInput; import ./index.less; const { Sider, Content } Layout; const ChatPage: React.FC () { const { currentSessionId, sessions, messagesMap, fetchSessions, loading } useChatStore(); const { sendStreamMessage } useChatStream(currentSessionId || ); const [inputLoading, setInputLoading] useState(false); // 初始化加载会话列表 useEffect(() { fetchSessions(); }, [fetchSessions]); const currentMessages currentSessionId ? messagesMap[currentSessionId] || [] : []; const handleSendMessage async (content: string) { if (!content.trim() || !currentSessionId) return; setInputLoading(true); try { await sendStreamMessage(content); } catch (error) { message.error(发送失败请检查网络或稍后重试); } finally { setInputLoading(false); } }; if (loading sessions.length 0) { return ( div style{{ textAlign: center, padding: 100px }} Spin sizelarge tip加载会话中... / /div ); } return ( Layout hasSider style{{ minHeight: 100vh }} {/* 左侧会话侧边栏 */} Sider width{280} themelight style{{ borderRight: 1px solid #f0f0f0 }} SessionSidebar / /Sider {/* 右侧主聊天区域 */} Layout Content style{{ display: flex, flexDirection: column }} {/* 消息列表区域 */} div style{{ flex: 1, overflow: auto, padding: 24px }} MessageList messages{currentMessages} / /div {/* 输入框区域 */} div style{{ padding: 16px 24px, borderTop: 1px solid #f0f0f0 }} ChatInput onSend{handleSendMessage} loading{inputLoading} disabled{!currentSessionId} / /div /Content /Layout /Layout ); }; export default ChatPage;5. 关键实现细节与避坑指南有了清晰的结构实现过程中还会遇到一些关键的技术点。以下是几个必须注意的细节5.1 流式响应(SSE)的稳定处理流式响应是AI对话体验的核心。除了上面Hook中的基本处理还需要注意连接保活与重连实现心跳机制在连接异常断开时自动重连。错误边界网络错误、服务端错误、解析错误要有不同的UI反馈。性能优化避免频繁的React状态更新导致卡顿可考虑使用useDeferredValue或防抖。5.2 文件上传的健壮性分片上传对于超大文件50MB实现分片上传和断点续传。并发控制限制同时上传的文件数量。格式校验在前端和后端双重校验文件类型和大小。解析状态轮询上传后文档需要后端解析和向量化这是一个长任务。前端需要轮询或使用WebSocket获取实时状态。// 示例轮询文档解析状态 const pollDocumentStatus async (docId: string, kbId: string) { const maxAttempts 60; // 最多轮询60次 const interval 2000; // 每2秒一次 for (let i 0; i maxAttempts; i) { await wait(interval); const status await fetchDocumentStatus(kbId, docId); if (status success) { message.success(文档解析完成); break; } else if (status error) { message.error(文档解析失败请检查文件格式。); break; } // 继续轮询... } };5.3 状态管理的边界Zustand Store拆分不要把所有状态放在一个Store里。按业务模块拆分如useChatStore、useKBStore、useUserStore。本地持久化使用zustand/middleware的persist中间件将会话列表等数据存到localStorage提升用户体验。避免重复请求在Store中缓存已获取的数据并设置合理的失效时间。6. 与后端联调API接口约定前端结构清晰了与后端的对接也要有章法。以下是一个关键接口的约定示例确保前后端协同顺畅。接口规范建议统一响应格式{ code: 0, message: success, data: {} // 或 [] }流式接口使用text/event-stream格式每个chunk是一个JSON字符串。错误码统一定义业务错误码如4001知识库不存在5001模型服务超时。核心接口列表GET /api/kb/list- 获取知识库列表POST /api/kb- 创建知识库POST /api/kb/{id}/document- 上传文档GET /api/kb/{id}/documents- 获取文档列表POST /api/chat/session- 创建会话GET /api/chat/sessions- 获取会话列表POST /api/chat/message/stream- 发送消息流式7. 部署与优化建议项目开发完成后部署和优化是最后一步也至关重要。7.1 构建与部署环境变量通过.env文件管理不同环境的API地址、密钥前缀等。Docker化编写Dockerfile和docker-compose.yml方便容器化部署。CI/CD集成GitHub Actions或GitLab CI实现自动化测试和部署。7.2 性能优化代码分割使用React.lazy和Suspense实现路由级和组件级懒加载。图片与资源优化压缩图片使用WebP格式配置合适的缓存策略。Bundle分析使用rollup-plugin-visualizer分析打包体积优化依赖。7.3 监控与错误追踪前端监控接入Sentry或Fundebug捕获运行时错误。用户体验监控监控页面加载时间FP, FCP, LCP、接口成功率。日志关键用户操作和异常情况打印日志便于问题排查。8. 总结从结构清晰到迭代从容通过以上从功能模块到代码目录的完整拆解我们可以看到构建一个企业级AI知识库前端核心挑战不在于某个炫酷的交互而在于对复杂状态和异步流程的架构管理。一个清晰的项目结构如本文推荐的api/、stores/、hooks/、components/分层带来的好处是长期的新人上手快目录即文档功能模块一目了然。协作冲突少按业务模块划分不同开发者负责不同Store和Component减少Git冲突。维护成本低状态逻辑集中在Store和Hook中UI组件保持纯净便于测试和重构。功能扩展易新增一个模块如“模型微调”只需按相同模式创建对应的api、store、page即可。当你按照这个结构将项目搭建起来后你会发现后续无论是添加“多模态文件预览”、“对话历史导出”还是“更复杂的权限模型”都可以在现有的框架内平滑地实现。这才是应对AI应用快速迭代的可持续开发之道。建议你将此结构作为模板保存在启动下一个AI相关前端项目时它将为你节省大量前期设计时间让你能更专注于业务逻辑和创新体验的实现。
返回列表