Skip to main content
Category:

In this article, we’ll look at how to define and use dynamic and async components in Vue.js.

keep-alive with Dynamic Components

We sometimes want to maintain the state or avoid re-rendering for performance reasons when we switch between components dynamically.

For example, we can use it as follows to keep the states of components when switching between them:

src/index.html :

Vue.component("post", {
  data() {
    return {
      showDetails: false
    };
  },
  template: `
    <div>
      <h1>Title</h1>
      <button @click='showDetails = !showDetails'>
        Toggle Details
      </button>
      <div v-if='showDetails'>
        Lorem ipsum dolor sit amet.
      </div>
    </div>
  `
});
Vue.component("archive", {
  data() {
    return {
      showDetails: false
    };
  },
  template: `
    <div>
      <h1>Archive</h1>
    </div>
  `
});new Vue({
  el: "#app",
  data: {
    tabName: "post"
  }
});

 

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click='tabName = "post"'>Post</button>
      <button @click='tabName = "archive"'>Archive</button>
      <keep-alive>
        <component v-bind:is="tabName"></component>
      </keep-alive>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

Then when we click Toggle Detail to show the text in the Post tab, we’ll see that it’ll still be shown after switching to the Archive tab and back.

This is because we wrapped keep-alive around our component element.

keep-alive requires that the components being switched between to all have names.

 

Async Components

We can create components that are loaded from the server only when it’s needed with async components.

To do this, instead of passing in an object as the second argument, we pass in a function that returns a promise instead.

For example, we can write the following:

src/index.js :

Vue.component("async-component", (resolve, reject) => {
  setTimeout(() => {
    resolve({
      template: "<div>Async component</div>"
    });
  }, 1000);
});
new Vue({
  el: "#app"
});

 

index.html :

<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <async-component></async-component>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>

Then we should see ‘Async component’ shown on the screen after 1 second.

We see that the function above takes a resolve function from the parameter and calls it when we want to retrieve the definition from the server.

To indicate loading has failed, we can call reject(reason) to do that.

Another example would be using it with Webpack code-splitting as follows:

Vue.component('async-webpack', function (resolve) {
  require(['./async-component'], resolve)
})

We can also use the import function to import a component as follows:

Vue.component(
  'async-webpack',
  () => import('./async-component')
)

If we want to register a component locally, we can write:

new Vue({
  // ...
  components: {
    'component': () => import('./async-component')
  }
})

 

Handling Loading State

We can return a promise that has an object to referencing components for loading and error.

For example, we can use it with an app that’s generated by the Vue CLI by running npx vue create app where app is the app name. Then we can select the default choices within the wizard.

Then we write the code as follows:

App.vue :

<template>
  <div id="app">
    <AsyncComponent/>
  </div>
</template>

<script>
import Loading from "./components/Loading";
import Error from "./components/Error";
import HelloWord from "./components/HelloWorld";

const AsyncComponent = () => ({
  component: new Promise(resolve => {
    setTimeout(() => {
      resolve(HelloWord);
    }, 1000);
  }),
  loading: Loading,
  error: Error,
  delay: 0,
  timeout: 3000
});

export default {
  name: "App",
  components: {
    AsyncComponent
  }
};
</script>

 

components/HelloWorld.vue :

<template>
  <div>Hello</div>
</template>

components/Error.vue :

<template>
  <div>Error</div>
</template>

component/Loading.vue :

<template>
  <div>Loading</div>
</template>

In App.vue , the following code:

component: new Promise(resolve => {
    setTimeout(() => {
      resolve(HelloWord);
    }, 1000);
  })

delays loading of the HelloWorld component for a second. delay is the number of milliseconds before the loading component is shown.

timeout is the number of milliseconds until Vue stops trying to load the component.

Therefore, we’ll see Loading from the Loading component as the HelloWorld component is loading.

This kind of component definition is available since Vue 2.3.0. Vue Router 2.4.0 should be used if we want to use the above syntax for route components.

Example 2:

const AsyncComponent = () => ({
  // The component to load (should be a Promise)
  component: import('./MyComponent.vue'),
  // A component to use while the async component is loading
  loading: LoadingComponent,
  // A component to use if the load fails
  error: ErrorComponent,
  // Delay before showing the loading component. Default: 200ms.
  delay: 200,
  // The error component will be displayed if a timeout is
  // provided and exceeded. Default: Infinity.
  timeout: 3000
})

 

Using with Suspense

Async components are suspensible by default. This means if it has a <Suspense> in the parent chain, it will be treated as an async dependency of that <Suspense>. In this case, the loading state will be controlled by the <Suspense>, and the component's own loading, error, delay and timeout options will be ignored.

The async component can opt-out of Suspense control and let the component always control its own loading state by specifying suspensible: false in its options.

Read also: https://codimth.com/blog/web/vuejs/render-defaultfallback-component-until-main-component-fetches-data-suspense-vuejs-3

Riadh Rahmi

Senior Web Developer PHP/Drupal & Laravel

I am a senior web developer, I have experience in planning and developing large scale dynamic web solutions especially in Drupal & Laravel.

Web Posts

Search

Page Facebook