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

Next.js项目部署Vercel遇HTMLVideoElement未定义错误求助

问题解决:Next.js + PhaserJS 部署Vercel时HTMLVideoElement is not defined错误

问题根源

报错是因为Next.js默认的**服务端渲染(SSR)**会在服务器环境下执行代码,而PhaserJS的内部依赖(比如requestVideoFrame.js)引用了浏览器专属的HTMLVideoElement对象,服务器环境不存在这个对象,导致构建失败。你之前在Home.js里加的判断只覆盖了Phaser初始化逻辑,但SceneMain.js在构建阶段就被服务器端加载解析了,触发了错误。

解决方案

核心思路是让Phaser相关代码完全只在客户端执行,禁止服务器端加载解析,通过Next.js的动态导入(dynamic)实现:

步骤1:修改Home.js的SceneMain导入方式

用Next.js的dynamic函数导入SceneMain,同时禁用SSR:

import { useEffect, useRef } from 'react';
import dynamic from 'next/dynamic';
import toast from '你的toast库路径';

// 动态导入SceneMain,强制仅在客户端加载
const SceneMain = dynamic(() => import('../scenes/SceneMain'), { 
  ssr: false,
  loading: () => <div>加载游戏中...</div> // 可选:添加加载占位符
});

const Home = () => {
    const gameContainer = useRef(null);
    const gameInstance = useRef(null);
    const loadingMessage = useRef(null);
    const notify = (text) => toast(text);

    useEffect(() => {
        // 动态导入Phaser,确保仅在客户端执行
        import('phaser').then((Phaser) => {
            const config = {
                type: Phaser.AUTO, 
                parent: gameContainer.current, 
                width: "75%",
                height: "84%",
                scene: [SceneMain],
                physics: {
                    default: 'arcade', 
                    arcade: {}
                }
            };

            gameInstance.current = new Phaser.Game(config);
            // 后续样式和事件逻辑保持不变
            gameContainer.current.style.borderRadius = '15px';
            gameContainer.current.style.overflow = 'hidden';
            gameContainer.current.addEventListener('click', () => {
                gameInstance.current.input.keyboard.enabled = true;
            });
            gameInstance.current.scene.scenes.forEach(scene => {
                scene.events.on('create', () => {
                    const mapWidth = 2208;
                    const mapHeight = 1408;

                    scene.cameras.main.setBounds(0, 0, mapWidth, mapHeight, true);
                    scene.cameras.main.setZoom(Math.min(gameInstance.current.scale.width / mapWidth, gameInstance.current.scale.height / mapHeight));
                    scene.cameras.main.centerOn(mapWidth / 2, mapHeight / 2);

                    loadingMessage.current.style.display = 'none';
                });
            });

            return () => {
                gameInstance.current.destroy(true);
            };
        });
    }, []);

    // 渲染部分示例
    return (
        <div>
            <div ref={loadingMessage}>加载中...</div>
            <div ref={gameContainer}></div>
        </div>
    );
};

export default Home;

步骤2:确保SceneMain.js仅在客户端执行

通过dynamic加ssr: false的配置,SceneMain.js的代码只会在客户端加载执行,不会触发服务器端的Phaser依赖解析,彻底避免HTMLVideoElement未定义的问题。

额外验证

  • 本地执行npm run build,确认构建过程无报错后再部署到Vercel
  • 检查next.config.js,确保没有强制SSR的特殊配置

为什么之前的判断没用?

你之前在Home.js里加的typeof HTMLVideoElement !== 'undefined'只覆盖了Phaser初始化逻辑,但SceneMain.js在构建阶段就被Next.js的SSR机制加载解析了,此时还没进入useEffect的客户端执行阶段,判断逻辑根本没生效。

内容的提问来源于stack exchange,提问作者Sneakers World

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:42:48