# ProjectResourcePanel 性能优化 **日期**: 2026-01-28 **类型**: 性能优化 **影响范围**: 前端 - 资源管理面板 ## 概述 基于 React Best Practices 对 `ProjectResourcePanel` 组件进行全面性能优化,包括 Bundle Size 优化、图片加载优化、Tab 切换优化和 Re-render 优化。 ## 优化内容 ### 1. Bundle Size 优化 **问题**:使用 barrel imports 导致不必要的代码打包 **解决方案**: - 拆分 `@/components/ui/dropdown-menu` 为直接导入 - 使用 `React.lazy` 延迟加载 Dialog 相关组件 - 仅在需要时加载删除确认弹窗 **代码变更**: ```typescript // Before import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; // After import { DropdownMenu } from '@/components/ui/dropdown-menu'; import { DropdownMenuContent } from '@/components/ui/dropdown-menu'; import { DropdownMenuItem } from '@/components/ui/dropdown-menu'; import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; // Dialog 延迟加载 const Dialog = lazy(() => import('@/components/ui/dialog').then(m => ({ default: m.Dialog }))); ``` **收益**: - 减少初始 bundle size - 提升首屏加载速度 - 删除弹窗按需加载 ### 2. 图片加载优化(已修正) **问题**: 1. 图片加载无过渡效果,用户体验差 2. 每次切换 tab,图片都重新显示 loading,即使已加载过 **解决方案**: - 为 `ResourceGridItem` 添加 `imageLoading` 状态 - 检查图片是否已在浏览器缓存中 - 已缓存的图片直接显示,不显示 loading skeleton - 使用 `loading="lazy"` 和 `decoding="async"` 优化加载 - 添加淡入动画(`transition-opacity duration-300`) **代码变更**: ```typescript const [imageLoading, setImageLoading] = useState(true); // 检查图片是否已在浏览器缓存中 const imageUrl = resource.thumbnailUrl || resource.fileUrl; const isImageCached = useMemo(() => { if (!imageUrl) return false; const img = new Image(); img.src = imageUrl; return img.complete && img.naturalHeight !== 0; }, [imageUrl]); // 如果图片已缓存,直接设置为已加载 useEffect(() => { if (isImageCached) { setImageLoading(false); } }, [isImageCached]); {/* Loading Skeleton - 仅在首次加载时显示 */} {imageLoading && !isImageCached && (
)} setImageLoading(false)} /> ``` **收益**: - 提升用户体验 - 避免图片突然出现 - 清晰的加载状态反馈 - 已缓存图片立即显示,无 loading 闪烁 - 利用浏览器原生懒加载优化性能 ### 3. Tab 切换优化(已修正) **问题**: 1. Tab 切换时频繁显示 loading,影响用户体验 2. Loading 和空状态同时闪烁 **解决方案**: - 添加 `loadedTabs` Set 记录已加载过的 tab - 仅首次加载时显示 loading - Loading 遮罩仅覆盖列表区域,不遮挡 tab 选项 - 优化渲染逻辑:loading → 内容(列表或空状态),避免同时显示 **代码变更**: ```typescript const [loadedTabs, setLoadedTabs] = useState>(new Set([RESOURCE_TYPES.CHARACTER])); // 标记当前 tab 为已加载 useEffect(() => { if (!isLoading && resources && resources.length >= 0 && !loadedTabs.has(filterType)) { const timer = setTimeout(() => { setLoadedTabs((prev) => new Set(prev).add(filterType)); }, 0); return () => clearTimeout(timer); } }, [isLoading, resources, filterType, loadedTabs]); // 判断是否需要显示 loading(首次加载且未加载过该 tab) const shouldShowLoading = isLoading && !loadedTabs.has(filterType); // 优化渲染逻辑:loading → 内容,避免闪烁 {shouldShowLoading ? ( ) : error ? ( ) : ( )} ``` **收益**: - 避免频繁的 loading 闪烁 - Tab 选项始终可见和可交互 - 更流畅的切换体验 - 仅首次加载显示 loading - 避免 loading 和空状态同时出现 ### 4. Re-render 优化 **问题**:不必要的 `useMemo` 和多个 `useEffect` 导致额外渲染 **解决方案**: - 简化不必要的 `useMemo`(常量数据、简单逻辑) - 合并相关的 `useEffect` - 移除空状态组件的 `useMemo`(JSX 本身已足够轻量) **代码变更**: ```typescript // Before const tabs: TabItem[] = useMemo( () => RESOURCE_CATEGORIES.map(...), [selectedProjectId] ); // After - RESOURCE_CATEGORIES 是常量,无需 memo const tabs: TabItem[] = RESOURCE_CATEGORIES.map(...); // Before - 两个独立的 useEffect useEffect(() => { /* projectId 变化 */ }, [selectedProjectId, ...]); useEffect(() => { /* 清除选中状态 */ }, [selectedResourceId, ...]); // After - 合并相关逻辑 useEffect(() => { // projectId 变化逻辑 // 清除选中状态逻辑 }, [selectedProjectId, filterType, searchQuery, selectedResourceId, selectedResources]); ``` **收益**: - 减少不必要的重渲染 - 简化代码逻辑 - 提升运行时性能 ### 5. 样式优化 **问题**:使用了非标准的 Tailwind 类名 **解决方案**: - 将 `bg-gradient-to-t` 修正为 `bg-linear-to-t` ## 性能指标 **预期提升**: - Bundle Size: 减少 ~5-10KB(Dialog 延迟加载) - 首屏加载: 提升 ~100-200ms - Tab 切换: 更流畅的 UI 响应 - 图片加载: 更好的用户体验 ## 测试建议 1. **Bundle Size 测试**: ```bash npm run build # 对比优化前后的 bundle size ``` 2. **功能测试**: - 切换不同 Tab,验证 loading 状态 - 上传图片,验证 loading skeleton - 删除资源,验证 Dialog 延迟加载 - 多选资源,验证选中状态 3. **性能测试**: - 使用 React DevTools Profiler 对比优化前后 - 验证 re-render 次数减少 - 验证 Tab 切换响应时间 ## 相关文件 - `client/src/components/features/project/ProjectResourcePanel.tsx` ## 参考 - React Best Practices Skill - [Vercel Engineering - React Performance Guidelines](https://github.com/vercel-labs/agent-skills/tree/react-best-practices/skills/react-best-practices)