18
votes

I have many components, i want to import then on demand. i have a drop down which actually contains components list, that what to load. I tried this example

<component :is="componentLoader"></component>

in script

componentLoader () {
  return () => import('@/components/testDynamic') 
}

testDynamic is a component name(For now i am trying with static component).

Getting this error

GET http://localhost:8080/0.js net::ERR_ABORTED 404
[Vue warn]: Failed to resolve async component: function () {
    return __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, "./src/components/testDynamic.vue"));
  }
  Reason: Error: Loading chunk 0 failed.

How to fix this? am i doing any thing wrong? or is there any other way to import components dynamically?

4
maybe try return () => require('@/components/testDynamic') - Andrew1325
or, register them globally - Andrew1325
require doesn't throw any error, but it is not rendering any thing. - Sam
@Andrew1325 Global registration means, it will load all the components in the global level right, all components loaded at once and can use any where. But they all load at once right initially? I want it be import on demand only. - Sam
Are you compiling your templates? cause maybe it's looking to import on the wrong place, i would suggest to try and load all 50 components, and see how big it really is, see if you even need a solution like the one you think you need... If you really need it, i think you would have to generate/compile each template individually, and then point the import to wherever you put them... - Erubiel

4 Answers

19
votes

You can register async dynamic components locally in a single file component like this:

export default {
  components: {
    'test-dynamic': () => import('@/components/testDynamic'),
    'other-dynamic': () => import('@/components/otherDynamic')
  },
  data () {
    return {
      current: 'test-dynamic'
    }
  }
}

And in your template:

<component :is="current"></component>

If you register multiple components then you would just change the value of current to the desired component.

In the case of many components, you can import an object mapping the component names to their respective file paths, then register them like:

import myComponents from '@/components'

export default {
  components: Object.keys(myComponents).reduce((obj, name) => {
    return Object.assign(obj, { [name]: () => import(myComponents[name]) })
  }, {})
  ...
}

Where myComponents is exported as:

// components/index.js
export default {
  foo: '@/path/to/Foo',
  bar: '@/path/to/Bar',
  ...
}
11
votes

I was having the same issue and because I was using Vue 3, none of the solutions given here worked for me. After some research, I found out that the procedure to define dynamic components (async components) is a little different in Vue 3. I hope this code helps someone.

<template>
    <component :is="comp"></component>
</template>

<script>
//Vue 3 is having a special function to define these async functions
import {defineAsyncComponent} from "vue";

export default {
 name: "DynamicComponent",
 //I am passing the name of the Component as a prop
 props: {
     componentName:{
         type: String,
         required: true
     }
 },
 computed: {
  comp () {
      return defineAsyncComponent(() => import(`@/components/${this.componentName}.vue`))
  }
}
}
</script>
0
votes

I use the following method a lot (Vue 2), it works well.

The following simple example allows a component that is to be loaded, to be specified dynamically: -because the call to launchEditForm just takes a string as a parameter. If you can formulate the filename as a string then you can load it.

It is important to keep launchEditForm as a "computed" property i.e. do not define it in "methods".

If the file that you call does not exist then you will get a runtime error along the lines of: Cannot find module './components/cliente/forms/DoesNotExist.vue'

<template>
  <div>
    <button
      type="button"
@click="launchEditForm('components/clients/forms/EditContactsForm.vue')"
    >
      Edit Contacts
    </button>
    <component :is="currentEditForm" />
  </div>
</template>
<script>
export default {
  name: 'Screen42',
  data() {
    return {
      componentToDisplay: null
    };
  },
  computed: {
    currentEditForm: function() {
      if (this.componentToDisplay) {
        return () => import(`@/${this.componentToDisplay}`);
      }
      return null;
    }
  },
  methods: {
    launchEditForm(fileName) {
      this.componentToDisplay = fileName;
    }
  }
};
</script>

Note in the above example the file resides at .../src/components/clients/forms/EditContactsForm.vue

Often this type of dynamic import would be done in a modal, and its just a case of moving the <component> tag as is, inside of the modal. Obviously we can use v-if's with boolean variables to open and close the modals as desired. But the code above should illustrate the core point -i.e that the component name can be dynamicly generated and the component dynamically loaded.

0
votes

what worked for me is awaiting componentLoader function since the import function returns a promise!

async componentLoader () {
  return await import('@/components/testDynamic') 
}