skip to Main Content

I have the following situation:

I have a component in my vue app that dynamically loads components depending on an API query. Here is my template:

<template>
  <main class="flex-1">
    <div class="py-6">
      <div class="px-4 sm:px-6 md:px-0 space-y-6">
        <LoadingComponent v-if="chosenSideBarItem === null" />
        <component v-else :is="getComponent(chosenSideBarItem.component)" />
      </div>
    </div>
  </main>
</template>

Here is my script part

const componentMap = {
  Comp1: "./components/Comp1.vue",
  Comp2: "./components/Comp2.vue",
  Comp3: "./components/Comp3.vue",
};

Object.entries(componentMap).forEach(([key, value]) => {
  asyncComponents[key] = defineAsyncComponent({
    loader: () => import(value),
    loadingComponent: LoadingComponent,
    delay: 200,
    errorComponent: ErrorComponent,
    timeout: 3000,
  })
});


function getComponent(name : string) {
  return asyncComponents[name];
}

So basically the app dynamically loads a given component depending on some string. This works perfectly fine on my dev environment, however if i try to deploy it in my docker container with nginx, i get a MIME error

Loading module from “http://localhost/assets/components/Comp1.vue” was blocked because of a disallowed MIME type (“text/html”).

Probably because that directory doesn’t exist. Here is my nginx.conf

worker_processes 4;

events { worker_connections 1024; }

http {
   server {
          listen       80;
          server_name  localhost;

          location / {
              root   /usr/share/nginx/html;
              index  index.html index.htm;
              include  /etc/nginx/mime.types;
              try_files $uri $uri/ /index.html;
          }
    }
}

Thanks in advance!

2

Answers


  1. Why not do it like this?

    components: {
        firstcomponent: defineAsyncComponent({
          loader: () => import('./firstcomponent.vue'),
        }),
        secondcomponent: defineAsyncComponent({
          loader: () => import('./secondcomponent.vue'),
        }),
      },
    

    this way the code is less complicated and gets loaded on demand when used in template

    usage in template would be like

    <component :is="componentName" />
    

    where componentName would be "firstcomponent" or "secondcomponent"

    If you have problems with repetition of the same code in multiple loaders, you can do

    const defaultParams = {
      delay: 20,
      timeout: 3000,
    }
    
    components: {
            firstcomponent: defineAsyncComponent({
              loader: () => import('./firstcomponent.vue'),
              ...defaultParams,
            }),
            secondcomponent: defineAsyncComponent({
              loader: () => import('./secondcomponent.vue'),
              ...defaultParams,
            }),
          },
    
    Login or Signup to reply.
  2. It is not clear whether you are using Vite or Webpack (Vue CLI) but both have a very similar limitations when using dynamic imports (import(...))

    Here is a documentation for @rollup/plugin-dynamic-import-vars used by Vite

    Most important limitation is based on the fact that import() must be processed by a bundler at compile time. So the bundler must be able to statically at compile time be able to analyze the argument and deduce what are the possible imports. Using variable is of course out of the question…

    So one way to fix it is this:

    const componentLoaders = {
      Comp1: () => import("./components/Comp1.vue"),
      Comp2: () => import("./components/Comp2.vue"),
      Comp3: () => import("./components/Comp3.vue"),
    };
    
    Object.entries(componentLoaders).forEach(([key, loader]) => {
      asyncComponents[key] = defineAsyncComponent({
        loader,
        loadingComponent: LoadingComponent,
        delay: 200,
        errorComponent: ErrorComponent,
        timeout: 3000,
      })
    });
    
    
    function getComponent(name : string) {
      return asyncComponents[name];
    }
    

    …or without using the map:

    function getAsyncComponent(name : string) {
      return defineAsyncComponent({
        loader: () => import(`./components/${name}.vue`) ,
        loadingComponent: LoadingComponent,
        delay: 200,
        errorComponent: ErrorComponent,
        timeout: 3000,
      })
    }
    
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search