NextJS构建加载.glb模型报错,如何处理客户端组件问题?
解决NextJS构建时加载GLB模型的URL解析错误问题
问题根源
开发环境正常但构建报错,核心原因是:
- 标记
"use client"后,NextJS仍会在构建阶段对组件进行服务端预渲染检查 - 服务端环境没有浏览器的URL上下文,直接使用相对路径
/models/xbox-regular.glb会被判定为无效URL,触发ERR_INVALID_URL错误
解决方案
方案1:静态导入模型文件(推荐)
直接通过import导入模型,让Webpack自动处理路径映射,避免服务端解析相对路径的问题:
- 修改组件代码,静态导入模型:
"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> ) }
- 更新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
相关产品推荐
相关产品推荐

