Improve Performance in Vue 3 Using Lazy Loading and Dynamic Import

Robin
Updated on March 27, 2023

Everyone wants to make their website fast. To do so we need to make sure that users download as less code as possible. That is why we want to divide your code into smaller chunks and load them on demand.

Lazy loading and dynamic import allow us to do exactly that. You can reduce initial loading time by sending a smaller chunk rather than giving everything at once.

In this article, I will demonstrate everything in detail about lazy loading and dynamic import in Vue 3. Here is what you will learn for this post:

  • What is Lazy Loading and when should you use it?
  • How does Lazy Loading imporove performance?
  • What is async component in Vue 3?
  • How to use async component in Vue 3?
  • Use Suspense with your async components
  • Lazily load your Vue 3 routes.
  • And many more...

What is Lazy Loading?

Lazy loading is a technique to load any resources on demand. That means, browsers only download resources like images or scripts only when they are needed. This technique reduces the initial load time of a webpage by not sending everything at once. So, no one has to download unnecessary things.

To lazily load our JavaScript code we use a feature called dynamic import which was first introduced in the JavaScript ES2020 version.

If you don't know about it, I will highly suggest you go through our complete guide on dynamic import in JavaScript because lazy loading is highly dependent on this feature.

Otherwise, it will be quite difficult to understand how lazy loading works behind the scene.

How Does Lazy Loading Improve Performance in Vue 3?

Mainly, in Vue 3 we lazily load our components and route to make sure our initial bundle size is smaller. In the case of large applications, this feature is important for performance.

When you lazy load your Vue components, they don't get added to the main bundle. Rather Vue creates smaller chunks for each of those components. In this way, your website users can download necessary components when they are needed.

As your initial bundle size is small, it will take less time to load. That is why your website will load faster and users don't have to download unnecessary code in the first place.

In Vue 3, you can load your components lazily using defineAsyncComponent function. When you load your components using this function, this will be called Async Component in Vue 3. We will discuss this topic in detail in this article later.

So, yes lazy loading improves performance quite a lot in your Vue 3 applications. When your website loads faster, your visitors don't leave which gets you more traffic.

Why Should You Use Lazy Loading Components?

You already know what lazy loading is and how it can improve your website performance. But you also should know why and when you may go for lazy loading components.

These are some of the reasons why you should use lazy loading for your Vue 3 components:

  • When your application size is large and you want to reduce your initial bundle size.
  • To increase your website performance and reduce loading time.
  • To reduce user data usage by sending as less code as possible.
  • To make your website more accessible on mobile devices with low-speed internet connections.
  • You can save your server bandwidth when you only send necessary resources to the users.
  • Fast page load leads to more visitors to your website because fewer users will leave your website.
  • As loading speed is a ranking factor in Google search, by improving performance you will have a better chance of ranking in Google.

What is Async Component in Vue 3?

Async Component is a feature provided by Vue 3 to load components dynamically on demand. Vue has a defineAsyncComponent function that helps to define which components it should load separately only when they are required. In this way, Vue does not include those components with the main bundle.

In Vue 3, this is the way we lazy load our components to increase the performance of our website.

How to Use Async Component in Vue 3

You know that to use the async component in Vue 3, you have to use the defineAsyncComponent function. Now the question is how can you use this function?

The defineAsyncComponent() function accepts another function that will return a promise. You know ES module dynamic import function i.e. import() also returns a promise.

Most of the time, to load components lazily in Vue 3 we use defineAsyncComponent() and import() functions together.

In my example, I will show how you can apply Async Component using both Compositions API with <script setup> and Options API. The Async component works in the same way with Options API.

Also Read: Composition API VS Options API in Vue 3 For Beginners

Composition API

          // /components/PostList.vue

<template>
    <div v-if="posts.length">
        <ul>
            <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
        </ul>
    </div>
</template>

<script setup>
import { onMounted, ref } from 'vue';

const posts = ref([]);

onMounted(async () => {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users/1/posts');
        posts.value = await response.json();
    } catch (err) {
        // Handle error
    }
})
</script>

        
          // /views/Home.vue

<template>
  <section class="container">
    <button class="btn" @click="togglePostList">Show Posts</button>

    <div v-if="isShowPosts">
      <PostList />
    </div>
  </section>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue';

const PostList = defineAsyncComponent(() => import('../components/PostList.vue'));

const isShowPosts = ref(false);

const togglePostList = () => {
  isShowPosts.value = !isShowPosts.value;
};
</script>

        

Suppose you have a button to display a list of posts. When users click on the button then they can see the list.

In this case, there is no need to add the PostList component to the main bundle because if a user does not click the button then that user will not see the list.

This is the perfect place where we can lazy load the component. So, I am using defineAsyncComponent() function. I am passing another function that is returning import() function with the path of PostList component.

Now, Vue will know that we want to load this component lazily so it will not add this component to the main bundle. Rather, Vue will create a separate file for this PostList component.

how to use async component for lazy loading in vue 3

Options API

          <template>
  <section class="container">
    <button class="btn" @click="togglePostList">Show Users</button>

    <div v-if="isShowPosts">
      <PostList />
    </div>
  </section>
</template>

<script>
import { defineAsyncComponent } from 'vue';
import LoadingComponent from '../components/LoadingComponent.vue';
import ErrorComponent from '../components/ErrorComponent.vue';

const PostList = defineAsyncComponent(() => import('../components/PostList.vue'));

export default {
  data() {
    return {
      isShowPosts: false
    }
  },
  components: {
    PostList
  },
  methods: {
    togglePostList() {
      this.isShowPosts = !this.isShowPosts;
    }
  },
}
</script>
        

In Options API, I can use defineAsyncComponent() function exactly in the same way as I did with Composition API. You just have to add the component name inside the components options.

If you are using Vite with your Vue project then you don't have to do anything. When you build your Vue project, Vite will create a file with the name of the component like PostList.js automatically.

          // /views/Home.vue

const PostList = defineAsyncComponent(() => import('../components/PostList.vue' /* webpackChunkName: "PostList" */));
        

But if you are using Webpack, by default Webpack will create a file with a random number like 0.js for the PostList component. If you want to rename this file, you need to use the Webpack magic comment.

For that, inside import() function after the path, just add a comment with a property webpackChunkName. The value you set for this property will be the name of the file. So you can set whatever name you want.

Async Component Options

The defineAsyncComponent() function not only accepts a function but also accepts other options as an object. You can pass an object with other options.

  • loader
  • loadingComponent
  • errorComponent
  • delay
  • timeout
          // /views/Home.vue

<script setup>
import { ref, defineAsyncComponent } from 'vue';
import LoadingComponent from '../components/LoadingComponent.vue';
import ErrorComponent from '../components/ErrorComponent.vue';

const PostList = defineAsyncComponent({
  loader: () => import('../components/PostList.vue'),
  loadingComponent: LoadingComponent,
  delay: 100,
  errorComponent: ErrorComponent,
  timeout: 2000
})
<script>

        

Here, the loader is the function that returns a promise. You can set the component path using the import() function.

You also can set a fallback component in loadingComponent property. This component will be shown while the main component is being loaded.

The property delay accepts time as a millisecond. The default value is 200ms. The value you set for this property will be the delay before showing the loading component.

If you provide an error component in errorComponent property, it will be shown in case the PostList component fails to load.

The timeout property also accepts time as a millisecond. The default value is Infinity. The time you set in this property will be the time Vue waits before showing the error component if the PostList component takes too long to load.

Also Read: How to Pass Data From Child to Parent in Vue 3 (Composition API)

Using Suspense Component in Vue 3 For Async Components

The async component can be used with the built-in <Suspense> component in Vue 3. This component has 2 slots. One is default slot and another one is fallback slot.

          // /views/Home.vue

<template>
    <section class="container">
        <button class="btn" @click="togglePostList">Show Posts</button>

        <Suspense v-if="isShowPosts">
            <template #default>
                <PostList />
            </template>
            <template #fallback>
                <p>Loading...</p>
            </template>
       </Suspense>
    </section>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue';

const PostList = defineAsyncComponent(() => import('../components/PostList.vue'));

const isShowPosts = ref(false);

const togglePostList = () => {
  isShowPosts.value = !isShowPosts.value;
};
</script>
        

While loading the PostList component, Vue will show the fallback slot content. When the main component is loaded successfully, it will show the main component.

The new <Suspense> component can be used alternatively if you don't want to use the other options available in defineAsyncComponent() function.

To know more about this you can go through the official suspense component documentation.

Lazy Load Vue 3 Routes

Another way to improve performance in Vue 3 is, to lazy load the routes. In this way, users will load codes for every route separately.

For example, your website has 2 routes /home and /about. If someone visits the home page, that user will only load codes of the home page. So, every page will have a separate file when you export your Vue 3 project.

          import { createRouter, createWebHistory } from 'vue-router';

const Home = () => import('../views/Home.vue');
const About = () => import('../views/About.vue');

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home,
        },
        {
            path: '/about',
            name: 'about',
            component: About,
        },
    ],
});

export default router;

        

In this example, I am using dynamic import using import() function instead of static import statement.

I am getting my component from a function that returns import() function with the component path. In this way, using dynamic import you can lazy load your routes in Vue 3.

how to lazy load routes in vue 3

But there is one issue. If you have 20 routes, you have to write import() function 20 times. This is not efficient because we are repeating the same code again and again.

To solve this you can write a function that will import the component for you. This way, you will write the function just one time.

          import { createRouter, createWebHistory } from 'vue-router';

const loadComponent = (view) => {
    return () => import(`../views/${view}.vue`);
}

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path: '/',
            name: 'home',
            component: loadComponent('Home'),
        },
        {
            path: '/about',
            name: 'about',
            component: loadComponent('About'),
        },
    ],
});

export default router;

        

Now, I have created loadComponent() function. This function accepts the name of the view. Inside the routes array, I can call the function with the name of the view. That's it.

The function will import the correct view for the route. Now I don't have to import 20 times for my 20 routes.

Also Read: Best Way to Re-render Vue 3 Components: Master Guide

Conclusion

In large applications, lazy loading plays an important role to improve website performance. You should try to use this feature as much as possible. Because it not only brings benefits for you but also gives a better user experience.

That is why I have shown you how you can lazy load your component and routes in Vue 3 using dynamic import and defineAsyncComponent() function.

I hope It was helpful and you will be able to use lazy loading in your next Vue 3 projects.

Thank you so much for reading. Happy coding :)

Related Posts