0

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

2
  • webpack or vite? Commented Nov 15, 2021 at 11:45
  • Using Vue CLI with Webpack Commented Nov 15, 2021 at 11:46

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

There's an error if I try to run your snippet. Anyway, that's more or less what I tried to do, isn't it?
@luis.ap.uyen what was the error? it does not show me error in my local development. and about your statement, yes that is similar to your method but I tried to remove the error of Cannot find module by using computed properties instead of "Vue methods".
The problem is: I'm showing a code cut short. In reality, I need to use methods instead of computed properties, because <component> is looped, for each open window, and I must pass the corresponding component name through <component :is="getComponent(window.component)"> since window actually comes from a prop called windows, which is an array containing each window object. I shortened it with a view to make it clear and come to the point.
Moreover, shouldn't I use defineAsyncComponent() in Vue3 as suggested in another answer?
I’m not sure about what you want to do, but as I know you can loop through windows object or array in a computed property also and then choose the window you want and set it as data variable in my answer to load the correct component. And about defineAsyncComponent I don’t know a lot about that feature of Vue3.js.
1

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`)),
    );
}

8 Comments

I tried what you suggested, and while the error has been fixed, I still get a warning and the component is not displayed yet. I have edited the question (EDIT 2) with the details based on your suggestion.
in the code you added you don't seem to register the component
how can I register a component whose name isn't known until its imported? If I were to manually register each and every component which will be imported, I should import all of them explicitly too, without lazy load. Otherwise I would get an error saying the registered component was not imported.
read again my code snippet, dynamic registration is handled. And please don't forget I'm trying to help you, I don't like the tone of your comment
I'm quite surprised, since I didn't mean to sound rude or in a bad tone whatsoever. Maybe I don't know to express myself in a better way in English. My apologies for that.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.