You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 09:15:28