- FastAPI backend with SQLModel, Alembic migrations, AgentScope agents - Next.js 15 frontend with React 19, Tailwind, Zustand, React Flow - Multi-provider AI system (DashScope, Kling, MiniMax, Volcengine, OpenAI, etc.) - All HTTP clients migrated from sync requests to async httpx - Admin-managed API keys via environment variables - SSRF vulnerability fixed in ensure_url()
10 KiB
10 KiB
前端优化建议
本文档基于对当前前端代码的检查,给出可执行的优化建议,按优先级和类别组织。
一、已修复问题
1. 项目设置保存 API 路径错误
- 位置:
frontend/src/components/canvas/panels/management/project-details/tabs/SettingsTab.tsx - 问题:使用
fetch(\${OpenAPI.BASE}/api/projects/${projectId}`)请求的是/api/projects/:id,而后端路由为/api/v1/projects/:id`,导致 404。 - 修复:改为使用生成的 API 客户端
DefaultService.updateProject(projectId, updateData),保证请求路径为/api/v1/projects/:id。 - 说明:若后端需要持久化
type、status、defaultImageModel等,需在UpdateProjectRequest中声明对应字段。
二、配置与一致性
2. API Base URL 与 Rewrites 统一(已落地)
- 现状:
next.config.mjs使用API_URL(无NEXT_PUBLIC_)做 rewrites。frontend/src/lib/client.ts使用NEXT_PUBLIC_API_URL || 'http://localhost:8000'设置OpenAPI.BASE。
- 已做:
client.ts改为OpenAPI.BASE = process.env.NEXT_PUBLIC_API_URL ?? '',默认走同源 + rewrites。next.config.mjs增加注释说明API_URL与NEXT_PUBLIC_API_URL的职责。- 新增
frontend/.env.example,说明API_URL(服务端 rewrites)与NEXT_PUBLIC_API_URL(浏览器端,不设则用同源)。
3. 依赖声明(已落地)
- 现状:
canvasStore.ts使用lodash-es(如debounce),但package.json中只有@types/lodash-es(devDependency),lodash-es为间接依赖。 - 已做:在
dependencies中显式添加lodash-es: ^4.17.21。
三、数据请求与状态
4. 项目页用 React Query 替代手写 fetch(已落地)
- 位置:
frontend/src/app/project/[id]/page.tsx - 已做:
- 新增
useProjectWorkspace(projectId)(useProjects.ts),内部使用useQuery+getProject(id, false, true),缓存 key 为projectKeys.workspace(id),与列表/详情隔离。 - 项目页改为使用该 hook,无 projectId 时展示「无效的项目」,loading 时展示
LoadingState,error 时展示错误文案 +「重试」按钮(调用refetch)。 - 数据同步到
useProjectStore在 hook 的useEffect中完成,依赖仅projectId与query.data,避免 setter 导致重复请求。
- 新增
5. 项目页初始化逻辑(已落地)
- 已做:在
useProjectWorkspace内用initialEpisodeSetRef保证「有集数据且当前无选中集时自动选中第一集」只执行一次;切换projectId时重置该 ref,避免 refetch 覆盖用户已选集。
四、性能与包体积
6. 大组件与动态加载(已落地)
- 现状:
Canvas.tsx、AppNode.tsx已对部分重量级组件使用next/dynamic(ImageCropper、SketchEditor、SettingsModal、ProjectDetailsModal、ExpandedView、RightSidebar、各 Node Renderer),方向正确。 - 已做:
- Canvas:底部输入栏
InteractionInput改为next/dynamic按需加载,首屏不阻塞;展示「加载输入栏...」占位。 - InteractionInput:
ChatDisplay改为next/dynamic,仅在用户打开「助手」聊天时加载对应 chunk。 - next.config.mjs:
optimizePackageImports增加@radix-ui/react-dialog、@radix-ui/react-dropdown-menu、@radix-ui/react-select、@radix-ui/react-tabs,与现有@lobehub/icons、lucide-react、framer-motion一起做 barrel 优化;可定期检查是否再加大库。
- Canvas:底部输入栏
7. 画布相关 memo 与订阅(已落地)
- 现状:Canvas 已对 Sidebar、RightSidebar、CanvasContextMenu 使用
memo;Zustand 需注意选择器粒度。 - 已做:
- 单字段订阅:画布相关 hooks 与组件中,凡只用 store 单一字段的,一律改为
useProjectStore((s) => s.xxx)/useCanvasStore((s) => s.xxx)(如useNodeExecution、useCanvasPersistence、useGenerationHistory、CanvasManager、StoryboardDetails、CreateAssetForm)。 - 多字段订阅:需要多个字段的改用
useShallow,避免整 store 订阅导致无关变更触发重渲染。已改:useNodeActions、useCanvasInitialization、useAssetSaver、StoryboardsTab、ScriptTab、AssetsTab、ProjectDetailsModal、EpisodeDetails、CanvasRightDock、OverviewTab、SettingsTab。 - 回调稳定性:上述从 store 取出的 action(如
updateAsset、setProject)由 Zustand 提供,引用稳定,子组件不会因回调引用变而多余重渲染。
- 单字段订阅:画布相关 hooks 与组件中,凡只用 store 单一字段的,一律改为
五、错误处理与健壮性
8. 使用 ErrorBoundary 包裹画布(已落地)
- 位置:
frontend/src/app/project/[id]/page.tsx - 已做:项目页已用
CanvasErrorBoundary包裹<Canvas />,画布渲染异常会展示 fallback,不再直接白屏。
9. 控制台与类型(进行中)
- 已做(第一批):
logger参数类型从any[]收紧为unknown[]。- 核心链路改造为
logger:OfflineSyncProvider、CanvasPersistenceService、useCanvasInitialization。
- 后续:其余
console.*与any仍较多,建议按模块持续替换(交互输入、项目管理、设置页优先)。
六、代码结构与可维护性
10. 超大类/文件拆分(进行中)
-
现状:
InteractionInput.tsx、AutoResizeTextarea.tsx等单文件超过 700 行,逻辑多、状态多。 -
风险:
- 修改任意一处逻辑都可能影响到完全无关的功能,容易产生回归。
- 代码评审成本高,新同学上手困难。
- 无法对局部逻辑做针对性单测或 Storybook。
-
建议(结构拆分):
- 按“Tab/模式”(如 text / image / video / audio)、或“编辑态 vs 创建态”拆成子组件或自定义 hooks(如
useInteractionEditMode、useGenerationSubmit),主文件只做组合与布局。 - 将 通用逻辑(如输入框自适应高度、回车发送、快捷键、草稿保存)沉淀为
useXXXhooks 或components/common/下的基础组件,避免在多个页面重复实现。 - 对于复用度低但逻辑复杂的块(如“生成参数面板”“节点执行状态展示”),放到
components/canvas/interaction/等子目录中,以“目录 + index 组件”形式归类。
- 按“Tab/模式”(如 text / image / video / audio)、或“编辑态 vs 创建态”拆成子组件或自定义 hooks(如
-
建议(渐进式重构策略):
- 以“先抽 hooks 再拆 UI”为原则:先把明显的纯业务逻辑(如表单状态、提交节流、API 调用)抽到 hooks,再将 View 拆分为多个视觉组件。
- 每次 PR 控制拆分粒度(例如一次只拆出一个 Tab 或一个大功能块),并配合简单的回归路径说明(“本次拆分仅影响 xx Tab 的输入与发送”)。
- 对已拆分出的 hooks/子组件,在命名与文件路径上保持稳定,避免后续再次大规模移动。
-
已做(第一步):
- 将
AutoResizeTextarea中 mention 解析与 chip 构建抽离到controls/interaction-input/mentionUtils.ts,减少主组件体积并提高复用性。
- 将
11. API 调用方式统一
- 现状:绝大多数使用
DefaultService.*,仅 SettingsTab 曾用裸fetch(已改为 DefaultService)。 - 风险:
- 手写 URL(如
/api/v1/...)容易与后端路由或版本前缀不一致,导致线上才暴露问题。 - 缺失统一的错误处理与类型约束,不利于后续做全局 toast、重试等能力。
- 手写 URL(如
- 建议(编码规范):
- 新功能 一律优先使用 生成的
DefaultService/各 Service,不再手写fetch+ 字符串路径。 - 仅在极少数场景(如第三方 webhook、中转代理)允许使用裸
fetch,并在代码上方用注释说明原因。 - 不在组件内直接使用
fetch,而是封装到frontend/src/lib/api/xxx.ts或对应 Service wrapper 中,组件只关心“调用哪个方法”。
- 新功能 一律优先使用 生成的
- 建议(基础设施):
- 如有需要,可在
lib/client.ts或单独文件中,封装统一的request/mutationhelper(例如整合 React Query、错误提示、埋点等),再由DefaultService在内部复用。 - 为常用的实体(如 Project、Episode、Asset)提供对应
useProjectQuery、useProjectMutation等 hooks,对外只暴露 hooks,不暴露底层 Service 调用细节。 - 在代码评审 Checklist 中增加一项:“是否使用了生成的 Service,而不是手写 fetch”,借此约束新增代码。
- 如有需要,可在
12. 国际化 (i18n)
- 现状:已接入 i18next,部分文案使用
useTranslation,同时存在大量中文硬编码。 - 建议:若计划多语言,逐步将界面文案迁到 i18n key;若短期仅中文,可在文档中说明当前策略,避免混用导致维护成本增加。
七、测试与质量
13. React Query 默认配置(已落地)
- 已做:
QueryProvider默认值调整为更保守策略:staleTime: 1min、retry: 1、refetchOnReconnect: true。- 在
useProjects/useProject/useProjectWorkspace上显式配置staleTime、retry、retryDelay、refetchOnReconnect;其中 workspace 查询单独设置staleTime: 60s,避免沿用全局一刀切。
14. 端到端与关键路径(进行中)
- 已做(基线):
- 新增 Playwright 配置:
frontend/playwright.config.ts。 - 新增关键路径 E2E:
frontend/tests/e2e/project-canvas.spec.ts(进入项目 → 载入画布 → 新增提示词节点 → 编辑内容 → 触发保存并断言保存请求)。 - 新增脚本:
frontend/package.json中e2e/e2e:headed。
- 新增 Playwright 配置:
- 后续:补充分镜编辑、素材拖拽、失败重试等场景。
八、小结优先级
| 优先级 | 项 | 说明 |
|---|---|---|
| 高 | 项目设置 API 路径 | 已修复 |
| 高 | 项目页 loading/error 与 React Query | 已落地 |
| 高 | 画布 ErrorBoundary | 避免白屏 |
| 中 | API Base 与 env 说明 | 减少部署/联调困惑 |
| 中 | lodash-es 显式依赖 | 依赖健康 |
| 中 | 大组件与动态加载 | 已落地 |
| 中 | 画布 memo 与细粒度订阅 | 已落地 |
| 中 | 大组件拆分(超大类拆 hooks/子组件) | 性能与可维护性 |
| 低 | console → logger、减少 any | 长期可维护性 |
| 低 | i18n 策略统一 | 若有多语言计划 |
如需对某一项做具体改动(例如贴出 patch 或分步实现),可以指定编号或文件路径继续细化。