|
@@ -0,0 +1,97 @@
|
|
|
+# Caveats 😱
|
|
|
+
|
|
|
+Our aim is to provide a simple way of using ThreeJS in VueJS with the best developer experience possible. However, there are some caveats that you should be aware of.
|
|
|
+
|
|
|
+## HMR and ThreeJS
|
|
|
+
|
|
|
+Hot module replacement (HMR) is a feature that allows you to update your code without reloading the page. This is a great feature that makes development much faster. **TresJS** uses [Vite](https://vitejs.dev/). However, is really tricky to make it work correctly with ThreeJS.
|
|
|
+
|
|
|
+Why? Because Tres builds the scene in a declarative way. This means that it creates the instance and add it to the scene when the component is mounted. The complexity comes to know when to remove the instance from the scene and when to add it again.
|
|
|
+
|
|
|
+Although a minimal disposal workflow is implemented, it is not perfect. This means that sometimes you will have to reload the page to see the changes correctly, specially when you are referencing an instances using [Template Refs](https://v3.vuejs.org/guide/component-template-refs.html)
|
|
|
+
|
|
|
+```vue
|
|
|
+<script setup lang="ts">
|
|
|
+const boxRef: Ref<TresInstance | null> = ref(null)
|
|
|
+
|
|
|
+onLoop(({ _delta, elapsed }) => {
|
|
|
+ if (boxRef.value) {
|
|
|
+ boxRef.value.rotation.y += 0.01
|
|
|
+ boxRef.value.rotation.z = elapsed * 0.2
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <TresMesh ref="boxRef" :scale="1" cast-shadow>
|
|
|
+ <TresBoxGeometry :args="[1, 1, 1]" />
|
|
|
+ <TresMeshStandardMaterial color="teal" />
|
|
|
+ </TresMesh>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+If you make a change on the `color` of the `TresMeshStandardMaterial` component, you will see that the change is applied but the rotation is not working anymore. This is because the instance is disposed and created again.
|
|
|
+
|
|
|
+:::tip
|
|
|
+So as **rule of thumb** you should reload the page whenever you don't see the changes you made.
|
|
|
+:::
|
|
|
+
|
|
|
+That being said we are working on a better solution for this 😁. If you have any idea how to solve this, please let us know.
|
|
|
+
|
|
|
+You can follow the discussion in [HMR Disposal Discussion](https://github.com/Tresjs/tres/issues/23)
|
|
|
+
|
|
|
+## Reactivity
|
|
|
+
|
|
|
+We all love reactivity 💚. It is one of the most powerful features of VueJS. However, we need to be mindful of it when using ThreeJS.
|
|
|
+
|
|
|
+Vue reactivity is based on [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This allows Vue 3 to automatically track changes to data objects and update the corresponding DOM elements whenever the data changes.
|
|
|
+
|
|
|
+Since we are rendering an scene and updating it in every frame (60FPS), that means that we are updating the scene 60 times per second. If the object to be updated is reactive, Vue will try to update the scene 60 times per second. This is not a good idea 😅 and will be detrimental for performance.
|
|
|
+
|
|
|
+Here is a benchmark of the difference between using a Proxy object and a plain object.
|
|
|
+
|
|
|
+<figure>
|
|
|
+ <img src="/proxy-benchmark.png" alt="Proxy vs Plain" style="width:100%">
|
|
|
+ <figcaption>Fig.1 - Executions per second Plan Object vs Proxy. </figcaption>
|
|
|
+</figure>
|
|
|
+
|
|
|
+Source: [Proxy vs Plain Object](https://www.measurethat.net/Benchmarks/Show/12503/0/object-vs-proxy-vs-proxy-setter)
|
|
|
+
|
|
|
+### Example
|
|
|
+
|
|
|
+❌ Incorrect
|
|
|
+
|
|
|
+```vue
|
|
|
+<script setup lang="ts">
|
|
|
+const position = reactive({ x: 0, y: 0, z: 0 })
|
|
|
+
|
|
|
+onLoop(({ _delta, elapsed }) => {
|
|
|
+ position.x = Math.sin(elapsed * 0.1) * 3
|
|
|
+})
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <TresMesh :position="position" cast-shadow>
|
|
|
+ <TresBoxGeometry :args="[1, 1, 1]" />
|
|
|
+ <TresMeshStandardMaterial color="teal" />
|
|
|
+ </TresMesh>
|
|
|
+</template>
|
|
|
+```
|
|
|
+
|
|
|
+✅ Correct
|
|
|
+
|
|
|
+```vue
|
|
|
+<script setup lang="ts">
|
|
|
+const position = { x: 0, y: 0, z: 0 }
|
|
|
+const boxRef: Ref<TresInstance | null> = ref(null)
|
|
|
+
|
|
|
+onLoop(({ _delta, elapsed }) => {
|
|
|
+ boxRef.value.position.x = Math.sin(elapsed * 0.1) * 3
|
|
|
+})
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <TresMesh ref="boxRef" :position="position" cast-shadow>
|
|
|
+ <TresBoxGeometry :args="[1, 1, 1]" />
|
|
|
+ <TresMeshStandardMaterial color="teal" />
|
|
|
+ </TresMesh>
|
|
|
+</template>
|
|
|
+```
|