Files
pixel/docs/FRONTEND_OPTIMIZATION.md
张鹏 f9f4560459 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()
2026-04-29 01:20:12 +08:00

166 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端优化建议
本文档基于对当前前端代码的检查,给出可执行的优化建议,按优先级和类别组织。
---
## 一、已修复问题
### 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 或分步实现),可以指定编号或文件路径继续细化。