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

React切换自定义播放器与Video.js时出现removeChild错误求助

React切换自定义播放器与Video.js时removeChild错误的解决办法

错误原因

当你初始化Video.js实例时,它会把原<video>元素从DOM里移除,替换成自己生成的容器结构。而你的代码里两个播放器共用同一个videoRef,且用了不同的key做条件渲染——切换回自定义播放器时,React会尝试移除已经被Video.js挪走的原<video>节点,DOM节点归属混乱就触发了Failed to execute 'removeChild' on 'Node'错误。

修复步骤

1. 给两个播放器分配独立的ref

别再共用一个videoRef,分开追踪自定义播放器和Video.js的视频节点:

const customVideoRef = useRef(null);
const vjsVideoRef = useRef(null);
const playerRef = useRef(null);
const [isCustomPlayer, setIsCustomPlayer] = useState(true);

2. 条件渲染时绑定对应ref

把两个ref分别绑到各自的<video>组件上,同时给它们不同的id避免冲突:

<div style={{ position: "relative" }}>
  {isCustomPlayer ? (
    // 自定义播放器
    <video
      key="custom-player"
      id="custom-video-player"
      className={isShortVideo ? "short-video-container" : "video-container"}
      ref={customVideoRef}
      src={videoData?.video_link}
      controls
      controlslist="nodownload"
      disablePictureInPicture
    ></video>
  ) : (
    // Video.js播放器
    <video
      key="video-js-player"
      id="vjs-video-player"
      ref={vjsVideoRef}
      className={`video-js vjs-default-skin vjs-big-play-centered ${
        isShortVideo ? "short-video-container" : "video-container"
      }`}
      controls
    ></video>
  )}
  <canvas ref={canvasRef} style={{ display: "none" }} />
  <button className="btn btn-toggle-player" onClick={handleTogglePlayer}>
    切换到{isCustomPlayer ? "Video.js" : "自定义"}播放器
  </button>
</div>

3. 修正useEffect逻辑

根据当前播放器类型,操作对应的ref,确保Video.js实例销毁干净:

useEffect(() => {
  if (isCustomPlayer) {
    // 切回自定义播放器时,销毁Video.js实例
    if (playerRef.current) {
      playerRef.current.dispose();
      playerRef.current = null;
    }
    // 可选:如果视频链接变化,重新加载自定义播放器
    if (customVideoRef.current && videoData?.video_link) {
      customVideoRef.current.src = videoData.video_link;
      customVideoRef.current.load();
    }
  } else {
    // 切换到Video.js时,初始化实例
    if (!playerRef.current && vjsVideoRef.current && videoData?.video_link) {
      playerRef.current = videojs(vjsVideoRef.current, {
        controls: true,
        autoplay: false,
        preload: "auto",
        sources: [{
          src: videoData.video_link,
          type: 'video/mp4'
        }]
      });
    }
  }

  // 组件卸载时清理Video.js实例
  return () => {
    if (playerRef.current) {
      playerRef.current.dispose();
      playerRef.current = null;
    }
  };
}, [isCustomPlayer, videoData]);

为什么之前的尝试没解决?

  • 共用ref导致React和Video.js对同一个DOM节点的操作不同步,节点已经被Video.js移走,React还在尝试操作它;
  • 虽然调用了dispose(),但React的DOM更新机制和Video.js的DOM篡改逻辑冲突,节点归属关系没理清楚。

内容的提问来源于stack exchange,提问作者Zephyr Sonic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:13:18