React(Next.js)使用Three.js创建的Mesh重复渲染且页面有滚动条如何解决
问题根源
- 你的代码直接把Three.js初始化逻辑写在组件函数体中,React组件每次重渲染都会执行这段逻辑,重复创建场景、渲染器实例,每次都往body插入新的canvas元素,所以会出现多个立方体。
- 滚动条是因为浏览器默认给body设置了margin,加上canvas默认是inline元素存在基线偏移,导致canvas实际占位尺寸超出视口。
解决方案
第一步:添加全局样式(放入项目的全局css文件,比如globals.css)
* { margin: 0; padding: 0; box-sizing: border-box; } body { overflow: hidden; } canvas { display: block; }
第二步:修改组件代码
'use client' // 仅Next.js 13+ App Router版本需要加这一行 import * as THREE from 'three'; import { useEffect, useRef } from 'react'; function Index() { // 存储容器节点的引用 const containerRef = useRef(null); useEffect(() => { // 仅组件挂载时执行一次初始化逻辑 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); // 插入到组件内部容器,不直接操作body containerRef.current.appendChild(renderer.domElement); const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); camera.position.z = 12; renderer.render(scene, camera); // 组件卸载时清理资源,避免内存泄漏 return () => { renderer.dispose(); containerRef.current.removeChild(renderer.domElement); geometry.dispose(); material.dispose(); }; }, []); // 空依赖数组保证初始化逻辑仅执行一次 return <div ref={containerRef}></div>; } export default Index;
说明
- 空依赖的useEffect只会在客户端组件挂载完成后执行一次,不会随组件重渲染重复执行,从根源避免了重复创建实例的问题。
- 把canvas插入到组件内部的ref容器中,符合React的DOM管理逻辑,不会在body中残留多余节点。
- 全局样式重置了默认边距、设置body溢出隐藏,canvas设为块级元素,完全避免滚动条出现。
内容的提问来源于stack exchange,提问作者Axisnix
相关产品推荐
相关产品推荐

