vuejs3vue-componentp5.js

Vue keeps p5 components alive after navigating


I have build a creativ coding website using vue and p5. In my app i display different p5 components depending on the current route. The problem is that somehow vue or p5 (idk which) keeps the components alive even after navigating to another RouterLink. that means that after each Route change there is one more Component runnung and updating and everything. Even multiple instances of the same Component if I navigate back and forth. Since some of my components, like a fractal tree generator, take a long time to calculate, the whole site gets slower and slower.

My setup is basically:

main.js

import "./assets/main.css";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import p5vue from "p5vue";

const app = createApp(App);
app.use(p5vue);
app.use(router);

app.mount("#app");

App.vue

<template>
    <NavBar />
    <RouterView />
</template>

router/index.js

import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      name: "home",
      component: () => import("../views/HomeView.vue"),
    },
    {
      path: "/component1",
      name: "Component1",
      component: () => import("../components/Component1.vue"),
    },
    {
      path: "/component2",
      name: "Component2",
      component: () => import("../components/Component2.vue"),
    },
  ],
});

export default router;

components/NavBar.vue

<script>
import { RouterLink } from "vue-router";

export default {
  components: {
    RouterLink,
  },
};
</script>
<template>
    <RouterLink :to="'/'">Home</RouterLink>
    <RouterLink :to="'/component1'">Component1</RouterLink>
    <RouterLink :to="'/component2'">Component2</RouterLink>
</template>

components/Component1.vue

<script setup>
const sketch = (p5) => {
  p5.setup = () => {
    p5.createCanvas(p5.windowWidth, p5.windowHeight);
    p5.background(127, 127, 127);
  };

  p5.mousePressed = () => {
    console.log("Component1 --> clicked")
  };

};
</script>

<template>
  <P5 :sketch="sketch" />
</template>

components/Component2.vue

<script setup>
const sketch = (p5) => {
  p5.setup = () => {
    p5.createCanvas(p5.windowWidth, p5.windowHeight);
    p5.background(127, 127, 127);
  };

  p5.mousePressed = () => {
    console.log("Component2 --> clicked")
  };

};
</script>

<template>
  <P5 :sketch="sketch" />
</template>

so now when i go to to Component1 with the router and click i get: "Component1 --> clicked" in the console. If i afterwards go to Component2 and click I don't get just "Component2 --> clicked", I get:

"Component1 --> clicked"
"Component2 --> clicked"

and if i go back to 1 I get all 3 in the same order that I visited them:

"Component1 --> clicked"
"Component2 --> clicked"
"Component1 --> clicked"

As I said, the stuff that actually happens is way more than just printing something to the console. and meaningfully slowes down everything.

Reloading the site clears everything up, at least until I navigate again.

I want the components to get destroyed completely and and not slow down the Webpage, but I am not sure were the problem is or how to fix it.


Solution

  • <script setup>
    import { onBeforeUnmount, ref } from "vue";
    let p5Instance = null;
    const sketch = (p5) => {
      p5Instance = p5;
    
      p5.preload = () => {};
    
      p5.setup = () => {};
    
      p5.draw = () => {};
    
      p5.mousePressed = () => {};
    
      p5.mouseDragged = () => {};
    
      p5.mouseReleased = () => {};
    
      p5.mouseWheel = (event) => {};
      p5.keyPressed = (event) => {
        switch (event.keyCode) {
          default:
            break;
        }
      };
    };
    
    // p5 instance Cleanup on unmount component
    onBeforeUnmount(() => {
      if (p5Instance) {
        p5Instance.remove();
      }
    });
    </script>
    
    <template>
      <P5 style="overflow: hidden; height: 100dvh"/>
    </template>
    

    If anyone comes across this post with the same problem i had, this is the solution I found, save the p5 instance in a global variable and use the on unmount hook to remove the instance on navigation.

    Sadly i dont remember where i found or who gave me this solution, otherwise i would give them some credit.

    @emeik has a very similar solution