VUE3 + Three.js 坑

1. 问题描述

将scene、camera、renderer、controls等变量用reactive变成响应式时,页面渲染会报错:

three.module.js?5a89:24471 Uncaught TypeError: 
'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 '#<Matrix4>' but got '[object Object]')
at renderObject (three.module.js?5a89:24471)
at renderObjects (three.module.js?5a89:24458)
at Proxy.WebGLRenderer.render (three.module.js?5a89:24258)
at animate (HelloWorld.vue?fdab:192)

2. 问题原因

Vue3的响应式原理是通过Proxy实现的,Proxy的get方法会拦截对象的读取操作,而three.js的源码中有很多属性是只读的,比如modelViewMatrix、normalMatrix等,这些属性在three.js的源码中是通过Object.defineProperty定义的,所以Proxy的get方法会拦截这些属性的读取操作,导致页面渲染报错。
在使用Proxy时,当属性存在属性特性configurable: false, value: undefined,时,则取其属性值时会报错

例如:

const handle = {
  get() {
    return 1;
  },
};

const obj = Object.create(null);
Object.defineProperty(obj, 'a', {
  configurable: false,
});
Object.defineProperty(obj, 'b', {
  value: undefined,
});
Object.defineProperty(obj, 'c', {
  configurable: true,
  value: 'c',
});

const proxy = new Proxy(obj, handle);
console.log(proxy.a); // 报错TypeError: 'get' on proxy: property 'a' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'undefined' but got '1')
console.log(proxy.b); // 报错Uncaught TypeError: 'get' on proxy: property 'b' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'undefined' but got '1')
console.log(proxy.c); // 1

3. 解决方案

  • 将scene、camera、renderer、controls等对象用全局var声明,不要reactive,这样就不会触发Proxy的get方法,也就不会报错了。
  • 在添加一些场景的时候,场景可以用ref定义。添加的时候用toRaw转一下,例如:
scene.add(toRaw(mesh.value))
renderer.value.render(toRaw(scene.value), camera.value);

如果没有了响应式,用Vue3还有什么意义呢?

长风破浪会有时,直挂云帆济沧海