现象
点击导航栏切换到"照片墙"页面,内容加载动画播完之后,整个组件闪一下(瞬间透明缩小再恢复),然后才正常显示。
真凶:useEffect 把首次加载当成了搜索过渡
⚠ 根因: useEffect 监听了 searchQuery,页面首次挂载时 searchQuery 为 ''(空字符串),effect 照常触发,把内容设成 opacity-0 scale-95,250ms 后再恢复。
这个 effect 是为"搜索时切换结果"设计的,但写的人没有排除首次加载的情况。
改前代码
useEffect(() => {
setIsTransitioning(true); // ← 页面一加载就藏内容
const timer = setTimeout(() => {
setActiveQuery(searchQuery.toLowerCase());
setIsTransitioning(false); // ← 250ms 后才恢复
}, 250);
return () => clearTimeout(timer);
}, [searchQuery]);
加载时序
页面内容渲染完成
↓
useEffect 触发 (searchQuery = '')
↓
setIsTransitioning(true)
↓
内容变为 opacity-0 + scale-95 ← 看不见了!
↓
250ms 后
↓
setIsTransitioning(false)
↓
内容恢复 opacity-100 + scale-100 ← 闪回来了!
改后代码
useEffect(() => {
if (!searchQuery) { // ← 空查询:跳过过渡
setActiveQuery('');
setIsTransitioning(false);
return;
}
setIsTransitioning(true); // ← 只有真正搜索时才过渡
const timer = setTimeout(() => {
setActiveQuery(searchQuery.toLowerCase());
setIsTransitioning(false);
}, 250);
return () => clearTimeout(timer);
}, [searchQuery]);
顺便修的两个问题
改动 2:去掉双层动画
外层 PageTransition 已经在做 CSS y:20→0 上滑动画。照片墙内部还有一层 AnimatePresence + motion.div 也在做 y:20→0。两层叠加:
- 视觉起始点变成
y:40(20 + 20) - 两个动画时长不同(0.35s vs 0.4s),不同步
- 退出动画时 framer-motion 操作 DOM 可能产生空帧
**方案:**删掉内层 AnimatePresence + motion.div,只保留外层 PageTransition。
改动 3:简化照片加载
原 PhotoImg 组件有骨架占位、加载失败状态、loading="lazy"、opacity 0→100 过渡。每个相册就一张封面图,这些花活没必要,去掉更干净。
经验
📌 "加载完成后闪一下" 这种 bug,说明不是加载过程的问题,是加载完后代码主动做了什么事。排查方向:搜首次挂载时会触发的 useEffect、setTimeout、AnimatePresence 退出回调。

