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

Next.js服务端渲染模式下Video.js运行异常问题求助

Next.js集成Video.js报错解决方案

问题根因

你遇到的黑屏后播放器消失、控制台输出VIDEOJS: WARN: The supplied element is not included in the DOM警告,由几个问题共同导致:

  • Video.js是纯浏览器端依赖,Next.js默认的服务端渲染逻辑会在DOM不存在的阶段尝试执行播放器初始化
  • React 18 严格模式下开发环境会重复执行useEffect挂载/销毁流程,原有销毁逻辑会提前删除DOM元素,导致二次初始化时找不到挂载节点
  • 原有组件useEffect依赖配置错误:依赖数组中加入了不会变化的ref对象,更新分支中直接使用未定义的player变量触发JS错误,同时缺失必要的依赖项导致闭包问题
  • 配置冲突:同时开启fluid: true自适应和固定宽高,会导致播放器尺寸渲染异常

修复代码

1. 修正ThorPlayer组件逻辑

// Next.js App Router 下必须加下面这行,Pages Router可省略
'use client';
import React from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

export const ThorPlayer = (props) => {
  const videoRef = React.useRef(null);
  const playerRef = React.useRef(null);
  const { options, onReady } = props;

  React.useEffect(() => {
    // 仅在播放器未初始化、DOM节点已挂载时执行初始化
    if (!playerRef.current && videoRef.current) {
      const player = videojs(videoRef.current, options, () => {
        player.log('player is ready');
        onReady?.(player);
      });
      playerRef.current = player;
    } else if (playerRef.current) {
      const player = playerRef.current;
      player.autoplay(options.autoplay);
      player.src(options.sources);
    }
    // 移除无意义的videoRef依赖,补充缺失的onReady依赖
  }, [options, onReady]);

  React.useEffect(() => {
    // 仅在组件真正卸载时销毁播放器,兼容React18严格模式
    return () => {
      if (playerRef.current) {
        playerRef.current.dispose();
        playerRef.current = null;
      }
    };
  }, []);

  return (
    <div data-vjs-player>
      <video 
        ref={videoRef} 
        className='video-js vjs-big-play-centered' 
        style={{ width: "800px", height: "400px" }}
      />
    </div>
  );
}

export default ThorPlayer;

2. 修正页面引入方式(禁用SSR)

不管是App Router还是Pages Router,都需要通过动态导入关闭组件的服务端渲染,避免服务端执行Video.js逻辑:

import React from 'react'
import dynamic from 'next/dynamic';
// 动态导入播放器组件,禁用SSR
const ThorPlayer = dynamic(() => import('../../components/_ThorPlayer'), { ssr: false });

export default function index() {
  const playerRef = React.useRef(null);

  const videoJsOptions = {
    autoplay: true,
    controls: true,
    responsive: true,
    fluid: false, // 和固定宽高冲突,需要自适应就删除内联宽高,把这个值改为true
    muted: true, // 浏览器自动播放策略要求,非静音状态下自动播放会被拦截
    sources: [{
      src: 'https://obj-gravscale.zadarazios.com:443/v1/AUTH_f57eb386f52e4dc0bcdf19764aecc205/ct/bl_se.mp4',
      type: 'video/mp4'
    }]
  };

  const handlePlayerReady = (player) => {
    playerRef.current = player;

    player.on('waiting', () => {
      player.log('player is waiting');
    });

    player.on('dispose', () => {
      player.log('player will dispose');
    });
  };

  return (
    <>
      <h1>Teste de Player: </h1>
      <ThorPlayer options={videoJsOptions} onReady={handlePlayerReady} />
    </>
  )
}

注意事项

  • 如果需要开启播放器自适应(fluid: true),删除video标签上的固定宽高内联样式即可,不要同时保留两个配置
  • 生产环境构建后React严格模式的重复执行问题会自动消失,但上述兼容逻辑不会影响生产环境运行
  • 若不需要自动播放,可以移除muted: true配置,否则用户进入页面默认静音

内容的提问来源于stack exchange,提问作者Abner Matheus Gomes Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:06:29