Home » vue » Module not found when trying to lazy load component file in Vue

Module not found when trying to lazy load component file in Vue

Posted by: admin November 26, 2021 Leave a comment

Questions:

I’m trying to implement a way to display components on demand by means of dynamic components and lazy load.

However when it comes to dynamically importing the component file, I get this error:

Error: Cannot find module ‘@/components/pages/UsersPage.vue’
at eval (eval at ./src/components/multiwindows lazy recursive

This is the component called Multiwindows (a cut short version), in which I try to load the selected component:

<template>
   <component :is="getComponent()"></component>
</template>
<script>
  export default {
     props: ['window'],
     methods: {
        getComponent(window) {
            const path = `@/components/pages/${this.window.component}.vue`;
            return import(path);
        }
    }
  }
</script>

window is an object passed as a prop including the data needed for the selected component. An example of it would be (note the component property which is the one taking place in the code above):

{
   name: 'Users',
   class: 'far fa-user',
   component: 'UsersPage',
}

The file structure of my project is like this:

  • src
    • components
      • multiwindows
        • MultiWindows.vue
      • pages
        • UsersPage.vue

I can’t get to asynchronously import UsersPage.vue, and it doesn’t matter if I change the path constant to a relative path, which since I’m importing from MultiWindows, should be like this:

const path = `../pages/${window.component}.vue`;

or if I try with an absolute path, both starting with / or not:

const path = `/src/components/pages/${window.component}.vue`;

EDIT:

I also noticed these errors in console:

sockjs.js?9be2:1687 WebSocket connection to
‘wss://mydomain/sockjs-node/919/1qzk04x2/websocket’ failed:

sockjs.bundle.js:1 Uncaught Error: Incompatible SockJS! Main site
uses: "1.5.2", the iframe: "1.5.0".

Chances are these errors are related to the module not being imported, but I don’t know how to fix them. I explicitly installed the sockjs-client package with NPM in case it was due to an outdated version. No success.

EDIT 2:

Adapting my code to what @jeremy-castelli suggested:

async getComponent(window) {
      const path = `@/components/pages/${this.window.component}.vue`;
      return await defineAsyncComponent(() => import(path));
}

I got no error now, but instead I get this warning, and the component is still not displayed:

Component is missing template or render function.

The loaded component has all the elements: template, script, etc. I checked defineAsyncComponent returns an AsyncComponentWrapper, which is in turn assigned to the :is attribute of the component

Answers:

I tested a solution for using dynamic component and lazy load them in Vue-js version 2. I’m not sure that it works for version 3. I used computed properties instead of Vue methods. Here is my index page code that passes the data to containerCompo component (the corresponding Multiwindows component in the question):

<template>
  <div class="about">
    <v-container
      class="px-0"
      fluid
    >

      <v-row>
        <v-col md="12">
          <container-compo :window1="compoData">
          </container-compo>
        </v-col>
      </v-row>
      
    </v-container>
  </div>
</template>

<script>
import containerCompo from "../components/containerCompo";
export default {
  data () {
    return {
      compoData: {
        name: 'Users',
        class: 'far fa-user',
        component1: 'dynamic1', // the name of dynamic component
      }
    }
  },
  components: {
    containerCompo,
  }
}
</script>

<style scoped>

</style>

the containerCompo has "window1" prop that accepts the data include the component name. Here is the code of containerCompo component:

<template>
<section>
    <component v-if="isCompoVisible" :is="getComponent"></component>
    <v-btn @click="isCompoVisible= true">click me</v-btn>
</section>
</template>

<script>

export default {
    name: 'containerCompo',
    data () {
        return {
            isCompoVisible: false,
        }
    },
    props: ["window1"],
    computed: {
        getComponent() {
            let data = this.window1.component1;
            return () => import(`./${data}`);
        }
    }
}
</script>

The getComponent computed data processes the window1 prop and extract the dynamic component name to be used in vue <component> tag. I supposed that containerCompo.vue and all dynamic components are in the same level in components folder of Vue and index.vue is in the views folder.

###

This is not the complete solution but it may help you

You need to register a component before using it
and there is also a method to dynamically import components defineAsyncComponent

import app from '@/main';
import { defineAsyncComponent } from 'vue';
....
 // Check if the component has already been registered.
 if (!app._context.components[componentName]) {
    app.component(
        componentName,
        defineAsyncComponent(() => import(`@/components/pages/${componentName}.vue`)),
    );
}