Correct way to use dynamic imports in Vite + Vue
Asked Answered
C

2

7

We have an app where we've been using a dynamic import syntax in our route definitions, like so:

  ...
  component: () => import('./components/SomeComponent'),

We recently transitioned to vite, and since the switch, we've been catching TypeError: Failed to fetch dynamically imported module whenever we deploy a new version of the app.

According to this SO post, this is happening because the hash for the files are invalidated on new deploys, but why are the files still being referenced with the previous hashes, when we are shipping a new build altogether?

I also found a previous issue that ran into the same error, and found this link in its thread that talked about how rollup expects a specific syntax for dynamic imports:

// current
component: () => import('./components/SomeComponent')

// expected?
component: () => import('./components/SomeComponent.vue')

Could this be why we are getting that Failed to fetch dynamically... error? Because my dynamic imports are missing the file extensions? I am a little confused, because the dynamic imports seem to still work even without the file extensions, it's just that we are catching errors on fresh deploys.

Canicula answered 17/1, 2023 at 15:3 Comment(1)
It's unlikely that the question can be certainly answered because the problem with build tool is complex and depends on many factors. But it causes less problems to explicitly specify .vue ext in imports in generalVanegas
J
4

but why are the files still being referenced with the previous hashes, when we are shipping a new build altogether?

The point of dynamic modules is that not all of the code is loaded to the browser. Let's take the following case, you have a website with 1 Dynamic module that is loaded with a button click.

When you build it, your files should look like this:

index.html
assets/
-- index-abc123.js
-- dynamicModule-aaa111.js
  • 'index-abc123.js' will have a reference to 'dynamicModule-aaa111.js'

So when a user will open your website, he should get the following

index.html
assets/
-- index-abc123.js

note that the user didn't load the dynamic module yet.

Now u will make a deployment with a slight change to the DynamicModule file and your file names will change to:

index.html
assets/
-- index-xxx345.js
-- dynamicModule-bbb222.js

Now the user who didn't refresh its browser will click on the button that should import the dynamic module. What will happen is that his browser will try to download 'dynamicModule-aaa111.js' and this file does not exist anymore and is replaced by 'dynamicModule-bbb222.js'. Now you will get the Error(Failed to fetch dynamically imported module)

Jungle answered 21/2, 2023 at 8:59 Comment(5)
Makes sense, if the changing hash of dynamically imported chunks is the problem, could one solution be to tweak the build settings so that the file paths for dynamically imported files are not given a random hash? i.e. just do dynamicModule.js instead of dynamicModule-{hash}.jsCanicula
well, you can, but caching-wise it will create a problem in your browser, Since 2 files (the old dynamicModule and the new one) will have the same name, when it will try to get the dynamicModule.js it will first check locally if the file is already cached (the old dynamicModule.js), and if so, it wouldn't get the new one.Jungle
This is a great analysis of the issue and why it happens. We're experiencing this too. How does one fix the issue?Raving
@Raving generally, a naive way to solve it is to catch the specific error with error boundary and force a page to reload.Jungle
I think the best way is to upload the new dist files without removing the old ones. The users will get the new deployment fast enough if your cache is set properly but users with old index.html will not get the errors. The problem is that all the "auto deploy" solutions I know doesn't work that way. Clouadflare pages and others always delete the old files and invalidate the cache. So you need your own custom deployment method \ setting up specific cache rules \ maybe setting up preload links? (npmjs.com/package/vite-plugin-preload)Hotze
T
0

The correct way to dynamically import components with Vue 3 is to use defineAsyncComponent() like described here: https://vuejs.org/guide/components/async.html#basic-usage

<template>
  <some-component></some-component>

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

<script>
import { defineAsyncComponent } from 'vue'

export default {
  components: { /* Directly in components */
    SomeComponent: defineAsyncComponent(() => import('./components/SomeComponent.vue'))
  },
  computed: { /* or as computed property, e.g. with variable */
    varComp() {
      return defineAsyncComponent(() => import(`./components/SomeComponent${this.compx}.vue`))
    }
  }
}
</script>
Tabbi answered 14/6, 2023 at 11:59 Comment(1)
This is route components, not dynamic components. They are not the same, and it is pointed out in the route docs to NOT use async components for routes. router.vuejs.org/guide/advanced/lazy-loading.htmlProtoxide

© 2022 - 2025 — McMap. All rights reserved.