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

Vue路由切换后Three.js Canvas消失问题求助

解决Vue3路由切换后Three.js Canvas消失的问题

核心原因分析

这个问题通常和Vue组件的生命周期或路由缓存机制有关:

  • 默认路由行为:切换路由时组件会被销毁,返回时重新创建。若Three.js初始化依赖的DOM元素未正确获取,或组件销毁时未清理Three.js资源,会导致重新初始化失败。
  • 组件被keep-alive缓存:此时切换路由不会销毁组件,返回时仅触发activated钩子,而初始化逻辑只在mounted中执行,导致Canvas未重新渲染。

针对性解决方案

情况1:组件未被缓存(默认路由场景)

确保组件销毁时清理Three.js资源,重新创建时正确初始化:

import * as THREE from 'three'

export default {
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null
    }
  },
  mounted() {
    this.initThree()
  },
  beforeUnmount() {
    // 清理Three.js资源,避免内存泄漏和冲突
    if (this.renderer) {
      this.renderer.dispose()
      this.scene.traverse(obj => {
        if (obj.geometry) obj.geometry.dispose()
        if (obj.material) obj.material.dispose()
      })
      // 移除Canvas元素
      const canvas = this.$el.querySelector('canvas')
      if (canvas) canvas.remove()
      // 重置实例引用
      this.scene = null
      this.camera = null
      this.renderer = null
    }
  },
  methods: {
    initThree() {
      // 获取组件根元素作为容器
      const container = this.$el
      // 初始化Three.js核心实例
      this.scene = new THREE.Scene()
      this.camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000)
      this.renderer = new THREE.WebGLRenderer()
      
      this.renderer.setSize(container.clientWidth, container.clientHeight)
      container.appendChild(this.renderer.domElement)
      
      // 添加测试内容(可替换为你的业务代码)
      const cube = new THREE.Mesh(
        new THREE.BoxGeometry(),
        new THREE.MeshBasicMaterial({ color: 0x00ff00 })
      )
      this.scene.add(cube)
      this.camera.position.z = 5
      
      // 启动渲染循环
      const animate = () => {
        requestAnimationFrame(animate)
        cube.rotation.x += 0.01
        cube.rotation.y += 0.01
        this.renderer.render(this.scene, this.camera)
      }
      animate()
    }
  }
}

情况2:组件被keep-alive缓存

将初始化逻辑移至activated钩子,同时在deactivated中暂停动画、清理临时资源:

import * as THREE from 'three'

export default {
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      animationId: null
    }
  },
  mounted() {
    this.initThree()
  },
  activated() {
    // 组件激活时,若实例已被清理则重新初始化
    if (!this.scene) {
      this.initThree()
    }
    // 恢复渲染循环
    if (!this.animationId) {
      this.startAnimation()
    }
  },
  deactivated() {
    // 暂停渲染循环,避免后台消耗资源
    if (this.animationId) {
      cancelAnimationFrame(this.animationId)
      this.animationId = null
    }
  },
  beforeUnmount() {
    // 组件彻底销毁时清理所有资源
    if (this.renderer) {
      this.renderer.dispose()
      this.scene.traverse(obj => {
        if (obj.geometry) obj.geometry.dispose()
        if (obj.material) obj.material.dispose()
      })
      const canvas = this.$el.querySelector('canvas')
      if (canvas) canvas.remove()
      this.scene = null
      this.camera = null
      this.renderer = null
    }
  },
  methods: {
    initThree() {
      const container = this.$el
      this.scene = new THREE.Scene()
      this.camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000)
      this.renderer = new THREE.WebGLRenderer()
      
      this.renderer.setSize(container.clientWidth, container.clientHeight)
      container.appendChild(this.renderer.domElement)
      
      const cube = new THREE.Mesh(
        new THREE.BoxGeometry(),
        new THREE.MeshBasicMaterial({ color: 0x00ff00 })
      )
      this.scene.add(cube)
      this.camera.position.z = 5
    },
    startAnimation() {
      const animate = () => {
        this.animationId = requestAnimationFrame(animate)
        const cube = this.scene.children[0]
        cube.rotation.x += 0.01
        cube.rotation.y += 0.01
        this.renderer.render(this.scene, this.camera)
      }
      animate()
    }
  }
}

调试技巧

  • 在生命周期钩子中添加console.log,确认钩子是否触发,比如:
    mounted() { console.log('首页组件已挂载') },
    beforeUnmount() { console.log('首页组件即将销毁') }
    
  • 切换路由后,在浏览器开发者工具的Elements面板检查:容器元素下是否存在Canvas,若存在则检查CSS样式(如display、opacity属性)是否导致不可见。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:35:20