Initial commit: Pixel AI comic/video creation platform
- 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()
This commit is contained in:
165
docs/FRONTEND_OPTIMIZATION.md
Normal file
165
docs/FRONTEND_OPTIMIZATION.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 前端优化建议
|
||||
|
||||
本文档基于对当前前端代码的检查,给出可执行的优化建议,按优先级和类别组织。
|
||||
|
||||
---
|
||||
|
||||
## 一、已修复问题
|
||||
|
||||
### 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 优化;可定期检查是否再加大库。
|
||||
|
||||
### 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 提供,引用稳定,子组件不会因回调引用变而多余重渲染。
|
||||
|
||||
---
|
||||
|
||||
## 五、错误处理与健壮性
|
||||
|
||||
### 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`),主文件只做组合与布局。
|
||||
- 将 **通用逻辑**(如输入框自适应高度、回车发送、快捷键、草稿保存)沉淀为 `useXXX` hooks 或 `components/common/` 下的基础组件,避免在多个页面重复实现。
|
||||
- 对于复用度低但逻辑复杂的块(如“生成参数面板”“节点执行状态展示”),放到 `components/canvas/interaction/` 等子目录中,以“目录 + index 组件”形式归类。
|
||||
- **建议(渐进式重构策略)**:
|
||||
- 以“**先抽 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、重试等能力。
|
||||
- **建议(编码规范)**:
|
||||
- 新功能 **一律优先使用** 生成的 `DefaultService`/各 Service,不再手写 `fetch` + 字符串路径。
|
||||
- 仅在极少数场景(如第三方 webhook、中转代理)允许使用裸 `fetch`,并在代码上方用注释说明原因。
|
||||
- 不在组件内直接使用 `fetch`,而是封装到 `frontend/src/lib/api/xxx.ts` 或对应 Service wrapper 中,组件只关心“调用哪个方法”。
|
||||
- **建议(基础设施)**:
|
||||
- 如有需要,可在 `lib/client.ts` 或单独文件中,封装统一的 `request`/`mutation` helper(例如整合 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`。
|
||||
- **后续**:补充分镜编辑、素材拖拽、失败重试等场景。
|
||||
|
||||
---
|
||||
|
||||
## 八、小结优先级
|
||||
|
||||
| 优先级 | 项 | 说明 |
|
||||
|--------|----|------|
|
||||
| 高 | 项目设置 API 路径 | 已修复 |
|
||||
| 高 | 项目页 loading/error 与 React Query | 已落地 |
|
||||
| 高 | 画布 ErrorBoundary | 避免白屏 |
|
||||
| 中 | API Base 与 env 说明 | 减少部署/联调困惑 |
|
||||
| 中 | lodash-es 显式依赖 | 依赖健康 |
|
||||
| 中 | 大组件与动态加载 | 已落地 |
|
||||
| 中 | 画布 memo 与细粒度订阅 | 已落地 |
|
||||
| 中 | 大组件拆分(超大类拆 hooks/子组件) | 性能与可维护性 |
|
||||
| 低 | console → logger、减少 any | 长期可维护性 |
|
||||
| 低 | i18n 策略统一 | 若有多语言计划 |
|
||||
|
||||
如需对某一项做具体改动(例如贴出 patch 或分步实现),可以指定编号或文件路径继续细化。
|
||||
Reference in New Issue
Block a user