# ADR 002: 分镜看板性能优化架构 ## 状态 提议中 (Proposed) ## 背景 当前分镜看板 API 一次性返回所有分镜数据,当项目包含 200+ 分镜时,会导致: - 首次加载慢(2-5秒) - 数据传输量大(300-500 KB) - 内存占用高 - 滚动性能差 ## 决策 采用**三层数据架构 + 虚拟滚动**的方案,实现高性能的分镜看板。 ### 1. 数据分层策略 #### Layer 1: 骨架数据(Skeleton API) **用途**:初始加载,获取所有分镜的基础信息 **接口**: ``` GET /api/v1/projects/{project_id}/storyboard-board/skeleton ``` **响应**: ```json { "projectId": "xxx", "totalDuration": 600, "storyboardCount": 200, "tracks": [ { "type": "storyboard", "items": [ { "id": "xxx", "startTime": 0, "endTime": 3, "title": "分镜 1" } ] } ] } ``` **数据量**:200 分镜 ≈ 10-20 KB #### Layer 2: 时间范围详细数据(Time Range API) **用途**:加载可视区域的详细数据 **接口**(已存在): ``` GET /api/v1/projects/{project_id}/storyboard-board/items/time-range ?startTime=0 &endTime=60 &trackTypes=storyboard,resource,video ``` **响应**: ```json { "items": [ { "id": "xxx", "type": "storyboard", "startTime": 0, "endTime": 3, "data": { "id": "xxx", "title": "分镜 1", "description": "...", "thumbnail_url": "...", "resources": {...} } } ] } ``` **数据量**:可视区域 20 分镜 ≈ 10-15 KB #### Layer 3: 单个分镜详细数据(已存在) **用途**:点击分镜时加载完整信息 **接口**: ``` GET /api/v1/storyboards/{storyboard_id} ``` ### 2. 前端架构 #### 数据管理 ```typescript // 使用 TanStack Query 管理三层数据 const { data: skeleton } = useStoryboardBoardSkeleton(projectId); const { data: visibleItems } = useStoryboardBoardTimeRange(projectId, timeRange); const { data: storyboard } = useStoryboard(selectedStoryboardId); ``` #### 虚拟滚动 ```typescript // 使用 react-window 实现虚拟滚动 import { VariableSizeList } from 'react-window'; {Row} ``` #### 时间范围计算 ```typescript // 根据滚动位置计算可视时间范围 const calculateVisibleTimeRange = (scrollLeft: number, containerWidth: number, pps: number) => { const startTime = scrollLeft / pps; const endTime = (scrollLeft + containerWidth) / pps; const buffer = 30; // 预加载 30 秒 return { startTime: Math.max(0, startTime - buffer), endTime: endTime + buffer }; }; ``` ### 3. 加载策略 #### 初始加载 1. 加载骨架数据(所有分镜基础信息) 2. 计算总时长和布局 3. 加载初始可视区域的详细数据 #### 滚动加载 1. 监听滚动事件(防抖 200ms) 2. 计算新的可视时间范围 3. 加载新区域的详细数据 4. 缓存已加载的数据 #### 预加载策略 - 预加载相邻 30 秒的数据 - 滚动方向预测(向右滚动时优先加载右侧) - 缩放时重新计算可视范围 ### 4. 缓存策略 ```typescript // TanStack Query 缓存配置 const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 分钟 cacheTime: 10 * 60 * 1000, // 10 分钟 }, }, }); ``` ## 实施计划 ### 阶段 1:后端 API(1-2 天) - [ ] 新增骨架数据接口 `/storyboard-board/skeleton` - [ ] 优化时间范围接口(已存在,需测试) - [ ] 添加响应压缩(gzip) ### 阶段 2:前端基础架构(3-4 天) - [ ] 实现骨架数据加载 - [ ] 实现时间范围加载 - [ ] 实现数据缓存管理 ### 阶段 3:虚拟滚动(3-5 天) - [ ] 集成 react-window - [ ] 实现虚拟滚动渲染 - [ ] 优化滚动性能 ### 阶段 4:优化和测试(2-3 天) - [ ] 性能测试(1000+ 分镜) - [ ] 内存泄漏检测 - [ ] 用户体验优化 **总计**:9-14 天 ## 性能目标 | 指标 | 当前 | 目标 | |------|------|------| | 首次加载时间 | 2-5s | <500ms | | 数据传输量 | 300-500 KB | <30 KB | | 内存占用 | 随分镜数增长 | 恒定 ~50 MB | | 滚动 FPS | 30-40 | 60 | | 支持分镜数 | <200 | 1000+ | ## 优势 1. **性能优异**:首次加载快,滚动流畅 2. **可扩展**:支持大规模数据(1000+ 分镜) 3. **用户体验好**:无感知加载 4. **架构清晰**:数据分层明确,易于维护 5. **向后兼容**:保留现有 API,渐进式升级 ## 劣势 1. **实施复杂度高**:需要前后端配合 2. **开发周期长**:预计 2-3 周 3. **测试成本高**:需要大量数据测试 ## 替代方案 ### 方案 A:仅移除 description 字段 - 优点:实施快(5 分钟) - 缺点:数据量仅减少 40-60%,无法支持大规模数据 ### 方案 B:分页加载 - 优点:实施简单 - 缺点:不适合时间轴场景,用户体验差 ## 参考资料 - [React Window 文档](https://react-window.vercel.app/) - [TanStack Query 文档](https://tanstack.com/query/latest) - [虚拟滚动最佳实践](https://web.dev/virtualize-long-lists-react-window/)