vue3结合three.js 展示3d模型时, 从a路由跳转到b路由,浏览器后退到a路由时,模型加载完后一片空白,并且自定义的CSS2DObject 标签 重复出现


  • 问题发现

这个问题是打包之后,上线到服务器上发现的,开发过程中却没有遇到,发现这个问题后,在开发模式下,去各种路由跳转然后回去也没有出现这个问题。说明这个问题只有打包后才会出现的!

问题这里展开说一下,标题只阐述了部分,问题罗列出来就是路由a跳转到b,再回到a时,模型加载空白, 并且自定义的css2dobject 标签会重复,并且重复a路由跳转b,再回去a, 页面就会变得很卡。多重复几次页面会卡死。 

  • 问题解决

大概花了半天时间去百度查阅资料,找到了问题的解决方案的一半【但是这个确实一个很棒的方案,某些时候你的问题可能就只需要一半】:

Three.js 内存释放问题_threejs在vue中内存泄露

为什么说是一半呢,确实通过这个老哥写的方法,内存问题得到了解决,a路由跳转到b路由,再回到的a,页面确实不卡了, 但是模型加载还是有问题,自定义的2d标签重复出现问题确实解决了。

首先是 通过控制台审查元素, 发现渲染3d容器下有两个canvas标签,由于离开a路由时没有把canvas给销毁掉,导致回到a路由时重新渲染的3d canvas 被第一次的挤到下方去,导致看不见【容器设置了 overflo: hidden】

所以另一半解决方案就是 再组件取消挂载前,把这个canvas给移除掉:

onBeforeUnmount(() => { // 组件移除之前触发
	try {
    document.querySelector('.pro3d-main').innerHTML = ''; // 3d容器,下面挂载着 canvas 容器,这里直接清空3d容器子元素
		scene.clear(); // 移除scene
		rt && rt.dispose() // 
        【rt 来源:import ResourceTracker  from '@/data/dispose.js' 

                  const rt = new ResourceTracker();
                  const track = rt.track.bind(rt);】
		renderer.value.dispose();
		renderer.value.forceContextLoss();
		renderer.value.content = null;
    
		cancelAnimationFrame(render)
		console.log(renderer.value.info)   //查看memery字段  都为0
        console.log(scene) //  查看scene ,children为空数组,表示 模型等销毁ok
	}catch (e) {
		console.log(e)
	}
})

// dispose.js
import * as THREE from 'three'
export default class ResourceTracker {
   constructor() {
   	this.resources = new Set();
   }
   track(resource) {
   	if (!resource) {
   		return resource;
   	}

   	// handle children and when material is an array of materials or
   	// uniform is array of textures
   	if (Array.isArray(resource)) {
   		resource.forEach(resource => this.track(resource));
   		return resource;
   	}

   	if (resource.dispose || resource instanceof THREE.Object3D) {
   		this.resources.add(resource);
   	}
   	if (resource instanceof THREE.Object3D) {
   		this.track(resource.geometry);
   		this.track(resource.material);
   		this.track(resource.children);
   	} else if (resource instanceof THREE.Material) {
   		// We have to check if there are any textures on the material
   		for (const value of Object.values(resource)) {
   			if (value instanceof THREE.Texture) {
   				this.track(value);
   			}
   		}
   		// We also have to check if any uniforms reference textures or arrays of textures
   		if (resource.uniforms) {
   			for (const value of Object.values(resource.uniforms)) {
   				if (value) {
   					const uniformValue = value.value;
   					if (uniformValue instanceof THREE.Texture ||
   						Array.isArray(uniformValue)) {
   						this.track(uniformValue);
   					}
   				}
   			}
   		}
   	}
   	return resource;
   }
   untrack(resource) {
   	this.resources.delete(resource);
   }
   dispose() {
   	for (const resource of this.resources) {
   		if (resource instanceof THREE.Object3D) {
   			if (resource.parent) {
   				resource.parent.remove(resource);
   			}
   		}
   		if (resource.dispose) {
   			resource.dispose();
   		}
   	}
   	this.resources.clear();
   }
}