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

NextJS构建加载.glb模型报错,如何处理客户端组件问题?

解决NextJS构建时加载GLB模型的URL解析错误问题

问题根源

开发环境正常但构建报错,核心原因是:

  • 标记"use client"后,NextJS仍会在构建阶段对组件进行服务端预渲染检查
  • 服务端环境没有浏览器的URL上下文,直接使用相对路径/models/xbox-regular.glb会被判定为无效URL,触发ERR_INVALID_URL错误

解决方案

方案1:静态导入模型文件(推荐)

直接通过import导入模型,让Webpack自动处理路径映射,避免服务端解析相对路径的问题:

  1. 修改组件代码,静态导入模型:
"use client"
import { OrbitControls, Stage } from '@react-three/drei'
import { Canvas, useLoader } from '@react-three/fiber';
import { Suspense, useMemo } from 'react';
import { Color, MeshToonMaterial } from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

// 根据组件所在位置调整导入路径,示例为组件在app/components下
import modelUrl from '../../public/models/xbox-regular.glb';

const Gltf = () => {
    const { scene, materials, nodes } = useLoader(GLTFLoader, modelUrl);

    useMemo(() => {
        const greyLevel = 0.06372078508138657;
        const replacementHandMaterial = new MeshToonMaterial({ color: new Color(greyLevel, greyLevel, greyLevel)});
        materials['Material.002'] = replacementHandMaterial;
        (nodes['RHand'] as any).material = replacementHandMaterial;
        (nodes['RHand001'] as any).material = replacementHandMaterial;
    }, [nodes, materials]);

    return (
        <div style={{ width: "100vw", height: "100vh" }}>
            <Canvas>
                <ambientLight intensity={0.25} />
                <Suspense fallback={null}>
                <Stage
                    preset="rembrandt"
                    intensity={1}
                    shadows="contact"
                    adjustCamera
                    environment="city">
                    <primitive object={scene} />
                </Stage>
                <OrbitControls makeDefault autoRotate />
                </Suspense>
            </Canvas>
        </div>
    )
}
  1. 更新Webpack配置,使用NextJS推荐的Asset Modules替代file-loader:
/** @type {import('next').NextConfig} */
const nextConfig = {
    webpack (config, options) {
        config.module.rules.push({
            test: /\.(glb|gltf)$/i,
            type: 'asset/resource',
            generator: {
                filename: 'models/[name][ext]' // 保持输出路径与public目录结构一致
            }
        })
        return config;
    },
    typescript: {
        ignoreBuildErrors: true
    }
}

module.exports = nextConfig

方案2:动态加载并限定客户端执行

通过useEffect延迟模型加载,确保仅在客户端浏览器环境中执行URL解析:

"use client"
import { OrbitControls, Stage } from '@react-three/drei'
import { Canvas, useLoader } from '@react-three/fiber';
import { Suspense, useMemo, useState, useEffect } from 'react';
import { Color, MeshToonMaterial } from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

const Gltf = ({ modelName }: { modelName: string }) => {
    const [modelData, setModelData] = useState<any>(null);

    useEffect(() => {
        const loadModel = async () => {
            // 拼接完整URL,利用浏览器上下文解析
            const fullModelUrl = `${window.location.origin}/models/${modelName}`;
            const data = await useLoader.promise(GLTFLoader, fullModelUrl);
            setModelData(data);
        };
        loadModel();
    }, [modelName]);

    if (!modelData) return null;

    const { scene, materials, nodes } = modelData;

    useMemo(() => {
        const greyLevel = 0.06372078508138657;
        const replacementHandMaterial = new MeshToonMaterial({ color: new Color(greyLevel, greyLevel, greyLevel)});
        materials['Material.002'] = replacementHandMaterial;
        (nodes['RHand'] as any).material = replacementHandMaterial;
        (nodes['RHand001'] as any).material = replacementHandMaterial;
    }, [nodes, materials]);

    return (
        <div style={{ width: "100vw", height: "100vh" }}>
            <Canvas>
                <ambientLight intensity={0.25} />
                <Suspense fallback={null}>
                <Stage
                    preset="rembrandt"
                    intensity={1}
                    shadows="contact"
                    adjustCamera
                    environment="city">
                    <primitive object={scene} />
                </Stage>
                <OrbitControls makeDefault autoRotate />
                </Suspense>
            </Canvas>
        </div>
    )
}

方案3:禁用组件的服务端渲染

如果需要快速绕过构建错误,可以在父组件中动态导入模型组件并禁用SSR:

// 父组件中导入
import dynamic from 'next/dynamic';

const Gltf = dynamic(() => import('./path-to-your-gltf-component'), {
    ssr: false,
    loading: () => <div>加载中...</div>
});

这样NextJS构建时会跳过该组件的服务端预渲染,直接在客户端加载执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:41:04