React实现短视频滚动切换URL时避免页面闪白问题求助
解决短视频滚动时URL更新导致的视频闪白问题
你遇到的闪白问题核心原因是:调用navigate()更新路由时,组件因params.id变化触发重新渲染,导致视频DOM元素被重建、视频重新加载;同时IntersectionObserver的逻辑存在变量未定义错误,进一步加剧了渲染异常。以下是具体修复方案:
1. 修正IntersectionObserver中的变量错误
原代码中if (params.id !== id)的id未定义,需从当前观测元素中获取视频ID:
const observer = new IntersectionObserver(entries => { entries.forEach((entry) => { const video = entry.target.querySelector("video") as HTMLVideoElement; // 从hidden input提取当前视频ID const contentId = entry.target.querySelector('input[name="contentId"]')?.value; if (!contentId) return; if (entry.isIntersecting) { // 仅当路由ID与当前视频ID不一致时更新路由 if (params.id !== contentId) { // 使用replace模式避免历史记录堆积,减少路由跳转副作用 navigate(contentId, { replace: true }); } video.play(); } else { video.pause(); } }); }, { threshold: 0.5 });
2. 阻止路由变化触发的不必要组件重渲染
当params.id变化时,组件会默认重新渲染,导致视频列表重建。用useMemo缓存渲染结果,仅在视频列表变化时更新:
return ( <div className="Videos"> {useMemo(() => { return videos.map((v, i) => <Content key={v.id} ref={refs.current[i]} video={v} />); }, [videos])} </div> );
同时调整Observer的绑定时机,仅在视频列表更新时重新绑定:
useEffect(() => { refs.current.forEach((ref) => { if (ref.current) { observer.observe(ref.current); } }); return () => { refs.current.forEach((ref) => { if (ref.current) { observer.unobserve(ref.current); } }); }; }, [videos]); // 仅当videos数组变化时执行
3. 优化视频加载策略,避免重新加载闪白
给视频标签添加预加载属性,修正标准属性拼写,确保视频元素稳定不被频繁销毁:
const Content = forwardRef(({ video }: { video: Video }, ref) => { const videoRef = ref as MutableRefObject<HTMLDivElement>; return ( <div ref={videoRef}> <video src={video.videoUrl} preload="auto" playsinline muted ></video> <input type="hidden" name="contentId" value={video.id} /> </div> ); });
4. 初始化路由时避免重复跳转
修正初始化跳转的变量错误,仅在路由无ID且有视频时执行跳转:
useEffect(() => { fetchVideos().then(fetchedVideos => { // 初始化refs数组 refs.current = fetchedVideos.map(() => createRef<HTMLDivElement>()); setVideos(fetchedVideos); // 仅当路由无ID且视频列表非空时跳转 if (!params.id && fetchedVideos.length > 0) { navigate(fetchedVideos[0].id, { replace: true }); } }).catch(error => { alert("error"); }); }, [params.id, navigate]); // 添加依赖项避免重复触发
额外优化:用状态跟踪激活视频,减少路由跳转频率
添加状态变量跟踪当前激活的视频ID,避免Observer频繁触发路由更新:
const [activeVideoId, setActiveVideoId] = useState<string | null>(null); const observer = new IntersectionObserver(entries => { entries.forEach((entry) => { const video = entry.target.querySelector("video") as HTMLVideoElement; const contentId = entry.target.querySelector('input[name="contentId"]')?.value; if (!contentId) return; if (entry.isIntersecting) { setActiveVideoId(contentId); if (params.id !== contentId) { navigate(contentId, { replace: true }); } video.play(); } else { // 仅当当前激活的是该视频时才暂停 if (activeVideoId === contentId) { video.pause(); } } }); }, { threshold: 0.5 });
通过以上调整,可彻底解决路由更新导致的视频闪白问题,同时优化滚动交互体验,匹配TikTok/YouTube Shorts的逻辑。
内容的提问来源于stack exchange,提问作者Ika
相关产品推荐
相关产品推荐

