Object3D in Vue 3.0

Recently, I want to use ThreeJs in Vue 3.0. However, there has been a problem I came across. When I init a new threejs’s object in data property in vue 3.0, such as Scene, Camera and so on, they will be transform into Proxy form. It will lead some attributes in these class can no be changed, because in the source code of threejs, the ‘defineProperty’ of Object is used, an is declared as ‘configurable: false’. Therefore, there will be a problem in console, which is ‘‘get’ on proxy: property ‘modelViewMatrix’ is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected ‘#’ but got ‘[object Object]’)’

<template>
  <div id="app">
    <div id="three-earth" ref="three-earth" />
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

export default {
  name: 'Earth',
  props: {
    
  },
  components: {

  },
  data() {
    return {
      container: null,
      render: null,
      camera: null,
      scene: null,
      raycaster: new THREE.Raycaster(),
      mouse: new THREE.Vector2(),
      controls: null
    }
  },

  mounted() {
    this.initRender()
    this.initScene()
    this.initCamera()
    this.initLight()
    this.initModel()
    // this.initGui()
    this.initControls()

    this.animate()
    window.onresize = this.onWindowResize
    window.addEventListener('click', this.onMouseClick, false)
  },
  methods: {
    initRender: function() {
      this.render = new THREE.WebGLRenderer({ antialias: true, alpha: true })
      this.render.setSize(this.$refs['three-earth'].offsetWidth, this.$refs['three-earth'].offsetHeight)

      this.render.shadowMap.enabled = true
      this.render.shadowMap.type = THREE.PCFSoftShadowMap
      this.render.setClearColor(0xffffff)

      this.container = document.getElementById('three-earth')
      this.container.appendChild(this.render.domElement)
    },
    initCamera: function() {
      this.camera = new THREE.PerspectiveCamera(75, this.$refs['three-earth'].offsetWidth / this.$refs['three-earth'].offsetHeight, 0.1, 1000)
      this.camera.position.set(0, 40, 100)
      this.camera.lookAt(new THREE.Vector3(0, 0, 0))
    },
    initScene: function() {
      this.scene = new THREE.Scene()
    },
    initLight: function() {

    },
    initModel: function() {
      var helper = new THREE.AxesHelper(10)
      this.scene.add(helper)

      var s = 25

      var cube = new THREE.CubeGeometry(s, s, s)

      for (var i = 0; i < 3000; i++) {
        var material = new THREE.MeshBasicMaterial({ color: this.randomColor() })

        var mesh = new THREE.Mesh(cube, material)

        mesh.position.x = 800 * (2.0 * Math.random() - 1.0)
        mesh.position.y = 800 * (2.0 * Math.random() - 1.0)
        mesh.position.z = 800 * (2.0 * Math.random() - 1.0)

        mesh.rotation.x = Math.random() * Math.PI
        mesh.rotation.y = Math.random() * Math.PI
        mesh.rotation.z = Math.random() * Math.PI

        mesh.updateMatrix()

        this.scene.add(mesh)
      }
    },
    initStats: function() {
      /* this.stats = new Stats()
      this.container.appendChild(this.stats) */
    },
    initControls: function() {
      this.controls = new OrbitControls(this.camera, this.render.domElement)
      this.controls.enableDamping = true
      this.controls.enableZoom = true
      this.controls.autoRotate = false
      this.controls.minDistance = 50
      this.controls.maxDistance = 200
      this.controls.enablePan = true
    },
    animate: function() {
      this.threeRender()
      // this.stats.update()
      this.controls.update()
      requestAnimationFrame(this.animate)
    },
    randomColor: function() {
      var arrHex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
      var strHex = '#'
      var index = ''
      for (var i = 0; i < 6; i++) {
        index = Math.round(Math.random() * 15)
        strHex += arrHex[index]
      }
      return strHex
    },
    onMouseClick: function(event) {
      this.mouse.x = ((event.clientX - 210) / this.$refs.secenContainer.offsetWidth) * 2 - 1
      this.mouse.y = -((event.clientY - 84) / this.$refs.secenContainer.offsetHeight) * 2 + 1

      // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
      this.raycaster.setFromCamera(this.mouse, this.camera)

      // 获取raycaster直线和所有模型相交的数组集合
      var intersects = this.raycaster.intersectObjects(this.scene.children)

      // 将所有的相交的模型的颜色设置为红色,如果只需要将第一个触发事件,那就数组的第一个模型改变颜色即可
      if (intersects.length > 0) {
        intersects[0].object.material.color.set(0xff0000)
      }
    },
    onWindowResize: function() {
      this.camera.aspect = this.$refs.secenContainer.offsetWidth / this.$refs.secenContainer.offsetHeight
      this.camera.updateProjectionMatrix()
      this.threeRender()
      this.render.setSize(this.$refs.secenContainer.offsetWidth, this.$refs.secenContainer.offsetHeight)
    },
    threeRender: function() {
      this.render.render(this.scene, this.camera)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#app{
  width: 100%;
  height: 100%;
}
#three-earth{
    width: 100%;
    height: 100%;
  }
</style>

i think you need to initialize objects in the created hook

created() {
this._scene = new THREE.Scene()

etc. it’s still dirty and you may want to check twice or else you could overwrite something vital. the problem of course is that it’s now not reactive any longer.

be very careful when dropping any foreign object into vue, before you know it the entire system down to the whole dom is proxied because objects have parent/child accessors. i moved away from vue and proxy magic because of that - couldn’t stand it anymore.