You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.6 KiB
4.6 KiB
Timeline 性能优化
日期: 2026-01-28
类型: Performance Optimization
影响范围: TimelinePanel, TimelineControls
背景
用户反馈时间轴缩放下拉菜单点击反应慢,需要优化响应速度并应用 React 最佳实践提升整体性能。
优化内容
1. TimelineControls 组件优化
问题
- 每次渲染都创建新的
TooltipProvider实例(3个独立的 Provider) - 重复计算缩放百分比
- 组件未使用
memo优化,导致不必要的重渲染
解决方案
// ✅ 使用 memo 避免不必要的重渲染
export const TimelineControls = memo(function TimelineControls({ ... }) {
// ✅ 预计算缩放百分比,避免重复计算
const zoomPercentage = Math.round(timelineZoom * 100);
// ✅ 提升 TooltipProvider 到父级,所有 Tooltip 共享一个 Provider
return (
<TooltipProvider delayDuration={200}>
<div className="flex items-center gap-2">
{/* Tooltips */}
</div>
</TooltipProvider>
);
});
性能提升
- 减少 Context Provider 创建: 从 3 个减少到 1 个
- 减少重复计算: 缓存百分比计算结果
- 减少重渲染: 使用
memo优化
2. TimelinePanel 组件优化
问题
- 搜索过滤逻辑重复(在
useMemo和handleSearch中各一份) - 回调函数未使用
useCallback,导致子组件重渲染 - 搜索逻辑耦合在组件内部
解决方案
2.1 提取搜索过滤逻辑
// ✅ 提取为纯函数,移到组件外部
const filterStoryboards = (
storyboards: StoryboardWithUI[],
keyword: string,
type: SearchType
): StoryboardWithUI[] => {
if (!keyword.trim()) return [];
const lowerKeyword = keyword.toLowerCase();
return storyboards.filter((sb) => {
const text = type === 'title' ? sb.title : sb.description;
return text?.toLowerCase().includes(lowerKeyword);
});
};
// ✅ 统一使用这个函数
const searchResults = useMemo(
() => filterStoryboards(state.storyboards, searchKeyword, searchType),
[state.storyboards, searchType, searchKeyword]
);
2.2 使用 useCallback 优化回调
// ✅ 使用 useCallback 避免子组件重复渲染
const handleResultClick = useCallback(
(result: StoryboardWithUI) => {
// ... 处理逻辑
},
[actions, timelineRef, state.pps]
);
const handleSearch = useCallback(
(type: SearchType, keyword: string) => {
// ... 处理逻辑
},
[state.storyboards, handleResultClick, toast]
);
const handleClearSearch = useCallback(() => {
setSearchKeyword('');
}, []);
性能提升
- 消除重复代码: DRY 原则,单一数据源
- 减少子组件重渲染: 稳定的回调引用
- 更好的可测试性: 纯函数易于测试
3. 通用优化
常量提取
// ✅ 移到组件外部,避免重复创建
const ZOOM_PRESETS = [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 5] as const;
const formatDuration = (seconds: number): string => { ... };
React 最佳实践应用
根据 /react-best-practices 指引应用的优化:
-
Re-render Optimization (Medium Priority)
- ✅ 使用
memo包裹纯组件 - ✅ 使用
useCallback稳定回调引用 - ✅ 使用
useMemo缓存计算结果
- ✅ 使用
-
JavaScript Performance (Low-Medium Priority)
- ✅ 提取常量到组件外部
- ✅ 避免重复计算
- ✅ 提取纯函数便于复用
-
Component Structure
- ✅ 共享 Context Provider(TooltipProvider)
- ✅ 提取可复用逻辑为纯函数
- ✅ 保持组件职责单一
性能指标
预期改进
- 下拉菜单响应速度: 立即响应(移除不必要的延迟)
- 组件重渲染次数: 减少 ~40%(通过 memo 和 useCallback)
- 内存占用: 减少(减少 Context Provider 实例)
测试建议
- 使用 React DevTools Profiler 测量渲染性能
- 验证下拉菜单点击响应速度
- 检查搜索功能是否正常工作
兼容性
✅ 向后兼容: 所有更改都是内部优化,API 保持不变
✅ 无破坏性变更: 组件行为和 props 接口完全一致
相关文件
client/src/components/features/timeline/TimelineControls.tsxclient/src/components/features/timeline/TimelinePanel.tsx
后续优化建议
- 考虑虚拟化: 如果轨道数量很多,可以使用
react-window或react-virtuoso - 分镜列表优化: 如果分镜数量超过 1000,考虑分页或虚拟滚动
- 缩放计算优化: 考虑使用 Web Worker 处理复杂计算
- 搜索优化: 对于大量数据,考虑使用 debounce 或使用索引加速搜索