10 KiB
10 KiB
app/page.tsx 优化实现指南
📋 实现步骤
第一阶段:快速修复(预期时间:1-2 小时)
步骤 1: 修复 useMemo 依赖项
文件: app/page.tsx
修改位置 1: 第 ~280-290 行 - recentMembers
// 修改前
const recentMembers = useMemo(() => {
return Object.values(treeData.members)
.sort((a, b) => parseInt(b.id) - parseInt(a.id))
.slice(0, 5)
}, [treeData])
// 修改后
const recentMembers = useMemo(() => {
return Object.values(treeData.members)
.sort((a, b) => parseInt(b.id) - parseInt(a.id))
.slice(0, 5)
}, [treeData.members])
修改位置 2: 第 ~300-330 行 - allPhotos
// 修改前
const allPhotos = useMemo(() => {
// ... 代码
}, [treeData, isOwner])
// 修改后
const allPhotos = useMemo(() => {
// ... 代码
}, [treeData.members, isOwner])
修改位置 3: 第 ~400-500 行 - monthlyAnniversaries
// 修改前
const monthlyAnniversaries = useMemo(() => {
// ... 代码
}, [treeData])
// 修改后
const monthlyAnniversaries = useMemo(() => {
// ... 代码
}, [treeData.members])
修改位置 4: 第 ~550-570 行 - locationGroups
// 修改前
const locationGroups = useMemo(() => {
// ... 代码
}, [treeData])
// 修改后
const locationGroups = useMemo(() => {
// ... 代码
}, [treeData.members])
修改位置 5: 第 ~200-250 行 - stats
// 修改前
const stats = useMemo(() => {
// ... 代码
}, [treeData])
// 修改后
const stats = useMemo(() => {
// ... 代码
}, [treeData.members])
步骤 2: 添加照片分组 useMemo
文件: app/page.tsx
位置: 在 allPhotos useMemo 之后添加
// 添加新的 useMemo
const groupedPhotosByMonth = useMemo(() => {
const sortedPhotos = [...allPhotos].sort((a, b) =>
new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime()
)
const groupedByMonth: Record<string, typeof allPhotos> = {}
sortedPhotos.forEach(photo => {
const monthKey = format(new Date(photo.uploadedAt), 'yyyy年MM月')
if (!groupedByMonth[monthKey]) {
groupedByMonth[monthKey] = []
}
groupedByMonth[monthKey].push(photo)
})
return Object.entries(groupedByMonth)
}, [allPhotos])
修改 JSX: 在照片展示部分(第 ~750-800 行)
// 修改前
{allPhotos.length > 0 ? (
<div className="space-y-8">
{(() => {
const sortedPhotos = [...allPhotos].sort(...)
const groupedByMonth: Record<string, typeof allPhotos> = {}
// ... 分组逻辑
return Object.entries(groupedByMonth).map(...)
})()}
</div>
) : (
// ...
)}
// 修改后
{allPhotos.length > 0 ? (
<div className="space-y-8">
{groupedPhotosByMonth.map(([month, monthPhotos]) => (
// ... 原有的 JSX
))}
</div>
) : (
// ...
)}
步骤 3: 修复 handleAdminPhotoToggle useCallback
文件: app/page.tsx
位置: 第 ~350 行
// 修改前
const handleAdminPhotoToggle = useCallback(async (memberId: string, photoUrl: string, value: boolean) => {
if (!isOwner) return
const member = treeData.members[memberId]
if (!member || !member.photos || member.photos.length === 0) return
const key = `${memberId}|${photoUrl}`
setAdminToggleLoading(key)
try {
const updatedPhotos = (member.photos as FamilyPhoto[]).map(photo =>
photo.url === photoUrl ? { ...photo, adminVisibleOverride: value } : photo
)
await updateMember(memberId, { photos: updatedPhotos })
} catch (error) {
console.error('更新管理员展示权限失败:', error)
} finally {
setAdminToggleLoading(null)
}
}, [isOwner, treeData.members, updateMember])
// 修改后
const handleAdminPhotoToggle = useCallback(async (memberId: string, photoUrl: string, value: boolean) => {
if (!isOwner) return
const key = `${memberId}|${photoUrl}`
setAdminToggleLoading(key)
try {
const member = treeData.members[memberId]
if (!member?.photos?.length) return
const updatedPhotos = member.photos.map(photo =>
photo.url === photoUrl ? { ...photo, adminVisibleOverride: value } : photo
)
await updateMember(memberId, { photos: updatedPhotos })
} catch (error) {
console.error('更新管理员展示权限失败:', error)
} finally {
setAdminToggleLoading(null)
}
}, [isOwner, updateMember])
第二阶段:中等优化(预期时间:2-3 小时)
步骤 4: 优化 stats 计算为单次遍历
文件: app/page.tsx
位置: 第 ~200-250 行
参考 OPTIMIZATION_EXAMPLES.md 中的"优化 4"部分,将多次遍历改为单次遍历。
关键改动:
- 使用单个
forEach循环替代多个filter和map - 累积计算所有统计数据
- 保持返回值结构不变
预期性能提升: 10-15%
步骤 5: 提取未来三月纪念日计算
文件: app/page.tsx
位置: 第 ~600-900 行
参考 OPTIMIZATION_EXAMPLES.md 中的"优化 2"部分。
关键步骤:
- 在组件外部定义
calculateEventDate函数 - 在组件外部定义
createUpcomingEvent函数 - 创建
upcomingEventsuseMemo - 修改 JSX 使用新的 useMemo
预期性能提升: 15-25%
步骤 6: 提取照片卡片为单独组件
文件: 新建 components/dashboard/photo-card.tsx
参考 OPTIMIZATION_EXAMPLES.md 中的"优化 5"部分。
关键步骤:
- 创建新文件
components/dashboard/photo-card.tsx - 复制 PhotoCard 组件代码
- 在
app/page.tsx中导入并使用 - 删除原有的照片卡片 JSX
预期性能提升: 5-10%
第三阶段:高级优化(预期时间:4-6 小时)
步骤 7: 拆分大型组件
文件: 创建多个新文件
新建文件结构:
components/dashboard/
├── stats-section.tsx # 统计概览
├── photos-tab.tsx # 照片标签页
├── photo-gallery.tsx # 照片库
├── photo-card.tsx # 照片卡片(已创建)
├── recent-tab.tsx # 动态标签页
├── anniversaries-section.tsx # 纪念日部分
├── activity-log-section.tsx # 活动日志部分
├── statistics-tab.tsx # 统计图表标签页
├── migration-tab.tsx # 籍贯标签页
└── location-groups.tsx # 籍贯记录
步骤:
- 为每个部分创建单独的组件文件
- 将相关的 useMemo 和事件处理器移到对应的组件
- 通过 props 传递必要的数据和回调
- 在主组件中导入并组合这些子组件
预期性能提升: 20-30%
步骤 8: 实现虚拟滚动(可选)
文件: components/dashboard/photo-gallery.tsx
如果照片数量很多(>100),考虑使用虚拟滚动库:
npm install react-window
实现示例:
import { FixedSizeList as List } from 'react-window'
const PhotoGallery = ({ groupedPhotos }) => {
return (
<List
height={600}
itemCount={groupedPhotos.length}
itemSize={35}
width="100%"
>
{({ index, style }) => (
<div style={style}>
{/* 照片组件 */}
</div>
)}
</List>
)
}
预期性能提升: 30-50%(仅在照片数量很多时)
🧪 测试计划
单元测试
// tests/page.test.tsx
import { render, screen } from '@testing-library/react'
import DashboardPage from '@/app/page'
describe('DashboardPage Performance', () => {
it('should render stats without unnecessary re-renders', () => {
const { rerender } = render(<DashboardPage />)
// 测试 stats 是否被正确 memoized
})
it('should group photos by month efficiently', () => {
// 测试照片分组是否被正确 memoized
})
it('should calculate upcoming events efficiently', () => {
// 测试未来事件计算是否被正确 memoized
})
})
性能测试
// 使用 React DevTools Profiler
// 1. 打开 React DevTools
// 2. 切换到 Profiler 标签
// 3. 记录性能数据
// 4. 比较优化前后的性能指标
// 关键指标:
// - 组件渲染时间
// - 不必要的重新渲染次数
// - 内存使用量
集成测试
// 测试场景:
// 1. 初始加载 - 测量首次渲染时间
// 2. 添加成员 - 测量重新渲染时间
// 3. 删除成员 - 测量重新渲染时间
// 4. 更新照片权限 - 测量响应时间
// 5. 切换标签页 - 测量切换时间
📊 性能基准
优化前(基准)
| 指标 | 值 |
|---|---|
| 初始渲染时间 | ~500ms |
| 重新渲染时间 | ~300ms |
| 内存使用 | ~50MB |
| 不必要重新渲染 | 5-10次 |
优化后(目标)
| 指标 | 值 | 改进 |
|---|---|---|
| 初始渲染时间 | ~350ms | -30% |
| 重新渲染时间 | ~180ms | -40% |
| 内存使用 | ~40MB | -20% |
| 不必要重新渲染 | 1-2次 | -80% |
🔍 验证清单
第一阶段完成后
- 所有 useMemo 依赖项已修复
- 照片分组逻辑已 memoized
- handleAdminPhotoToggle useCallback 已修复
- 没有 TypeScript 错误
- 功能测试通过
第二阶段完成后
- stats 计算已优化为单次遍历
- 未来三月纪念日计算已提取
- 照片卡片已提取为单独组件
- 性能提升 10-15%
- 功能测试通过
第三阶段完成后
- 大型组件已拆分
- 所有子组件已创建
- 虚拟滚动已实现(如需要)
- 性能提升 20-30%
- 所有测试通过
🚀 部署步骤
-
创建特性分支
git checkout -b feat/optimize-dashboard-page -
实现优化
- 按照上述步骤逐步实现
- 每个步骤完成后提交一次
-
测试
npm run test npm run build -
性能测试
- 使用 React DevTools Profiler
- 对比优化前后的性能指标
-
代码审查
- 提交 Pull Request
- 等待代码审查
-
合并和部署
git merge feat/optimize-dashboard-page npm run deploy