preface

One problem with single-page apps is the slow rendering of the first screen. This is because the server sends a lot of JavaScript to the client when the page first loads, which must be downloaded and parsed before anything can be displayed on the screen. You can imagine that as your application grows in size, the impact of this problem on the user experience becomes more pronounced.

Now fortunately, when building Vue applications using the Vue CLI (using Webpack), there are steps you can take to counteract this. In this article, I’ll show you how to load parts of a page after the initial rendering of an application using asynchronous components and webPack’s code-splitting capabilities. This will minimize initial load times and provide a better user experience for your application.

Learning about asynchronous components

Before we start creating asynchronous components, let’s look at how we normally load components. To do this, we’ll use a very simple message component as an example:

<! -- Message.vue --> <template> <h1>New message! </h1> </template>Copy the code

Now that we have created our Message component, let’s load it into our file and display it. We can import the component and add it to the component options so that we can use it in the template:

<! -- App.vue --> <template> <div> <message></message> </div> </template> <script> import Message from"./Message";
export default {
  components: {
    Message
  }
};
</script>
Copy the code

But what happens now? The Message component is loaded as soon as the application loads, so it is included in the initial loading process.

This may not seem like a big deal for a simple application, but consider a complex scenario like an e-commerce site. Imagine that a user adds an item to a shopping cart and then wants to check out, so clicking the check out button generates a box with all the details of the selected item. Using the method above, this checkout box will be included in the initial package, but we only need to use this component when the user clicks the checkout button. Users can even navigate the site without clicking the checkout button, which means it doesn’t make sense to waste resources while loading a component that might not be used.

To improve application efficiency, we can combine lazy loading and code splitting techniques.

Webpack provides code splitting capabilities that allow you to break your code into bundles that can then be loaded on demand or later loaded in parallel. It can only load specific snippets of code when needed or used.

Dynamic Imports

Vue uses Dynamic Imports to solve this situation. This feature introduces a new function-like import form that returns promises containing (Vue) components. Since import is a function that accepts strings, we can do powerful things like load modules using expressions. Dynamic imports have been available in Chrome since version 61. For more information on these, visit the Google Developers website.

Code splitting is handled by bundlers such as Webpack, Rollup, or Parcel, which parse the dynamic import syntax and create separate files for each dynamically imported module. We’ll see this later in the network TAB of the console. But first, let’s look at the differences between static and dynamic imports:

// static import
import Message from "./Message";

// dynamic import
import("./Message").then(Message => {
  // Message module is available here...
});
Copy the code

Now, let’s apply this knowledge to our Message component, which we’ll get as follows: app.vue

<! -- App.vue --> <template> <div> <message></message> </div> </template> <script> import Message from"./Message";
export default {
  components: {
    Message: () => import("./Message")}}; </script>Copy the code

As you can see, the function import() resolves the Promise that returns the component, which means that we have successfully loaded the component asynchronously. If you look at the DevTools Network TAB, you’ll notice a file called 0.js that contains asynchronous components.

Load asynchronous components based on conditions

Now that we’ve mastered the asynchronous components, let’s load them only when we really need them. In the previous section of this article, I explained the use case for a checkout box that loads only when the user clicks the checkout button. Let’s build it out.

Project Settings

If you don’t have vue/ CLI installed, you should install it first:

npm i -g @vue/cli
Copy the code

Next, use the CLI to create a new project and select Default when prompted:

vue create my-store
Copy the code

Go to the project directory and install the Ant-Design-vue library we will use for styles:

cd my-store
npm i ant-design-vue
Copy the code

Next, import the Ant design library: SRC /main.js

import 'ant-design-vue/dist/antd.css'
Copy the code

Finally, we create two new components in SRC /comonents: checkout. vue and Items. Vue:

touch src/components/{Checkout.vue,Items.vue}
Copy the code

Write a store view layer

Open SRC/app. vue and replace the file with the following code:

<template>
  <div id="app">
    <h1>{{ msg }}</h1>
    <items></items>
  </div>
</template>

<script>
import items from "./components/Items"

export default {
  components: {
    items
  },
  name: 'app'.data () {
    return {
      msg: 'My Fancy T-Shirt Store'
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

h1, h2 {
  font-weight: normal;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>
Copy the code

There’s nothing fancy here. All we do is display a message and render a

component.

Next, open SRC/Components/kitems.vue and add the following code:

<template>
  <div>
    <div style="padding: 20px;">
      <Row :gutter="16">
        <Col :span="24" style="padding:5px">
          <Icon type="shopping-cart" style="margin-right:5px"/>{{shoppingList.length}} item(s)
          <Button @click="show = true" id="checkout">Checkout</Button>
        </Col>
      </Row>
    </div>
    <div v-if="show">
      <Row :gutter="16" style="margin:0 400px 50px 400px">
        <checkout v-bind:shoppingList="shoppingList"></checkout>
      </Row>
    </div>
    <div style="background-color: #ececec; padding: 20px;">
      <Row :gutter="16">
        <Col :span="6" v-for="(item, key) in items" v-bind:key="key" style="padding:5px">
          <Card v-bind:title="item.msg" v-bind:key="key">
            <Button type="primary" @click="addItem(key)">Buy ${{item.price}}</Button>
          </Card>
        </Col>
      </Row>
    </div>
  </div>
</template>

<script>
import { Card, Col, Row, Button, Icon } from 'ant-design-vue';

export default {
  methods: {
    addItem (key) {
      if(! this.shoppingList.includes(key)) { this.shoppingList.push(key); } } }, components: { Card, Col, Row, Button, Icon, checkout: () => import('./Checkout')
  },
  data: () => ({
    items: [
      { msg: 'First Product', price: 9.99}, {MSG:'Second Product', price: 19.99}, {MSG:'Third Product', price: 15.00}, {MSG:'Fancy Shirt', price: 137.00}, {MSG:'More Fancy', price: 109.99}, {MSG:'Extreme', price: 3.00}, {MSG:'Super Shirt', price: 109.99}, {MSG:'Epic Shirt', price: 3.00},], shoppingList: [], show:false
  })
}
</script>
<style>
#checkout {
  background-color:#e55242;
  color:white;
  margin-left: 10px;
}
</style>
Copy the code

In this file, we display a shopping cart icon with the number of items. The item is extracted from the Items array. If you click the Buy button for an item, the addItem method is called, which pushes the related item into the shoppingList array. Thus increasing the total number of shopping carts.

We also added a Checkout button to the page:

<Button @click="show = true" id="checkout">Checkout</Button>
Copy the code

When the user clicks this button, we set the parameter show to true. True is very important for conditional loading of our asynchronous components.

In the next few lines, you can find the v-if declaration, which only displays the

of our checkout component, but we only want to display the checkout component when the user clicks the checkout button. What do we do?

Here we load the Checkout component asynchronously in the Components option. Here v-bind passes the parameters to the component. As you can see, creating conditional asynchronous components is easy:

<div v-if="show">
  <checkout v-bind:shoppingList="shoppingList"></checkout>
</div>
Copy the code

Let’s take a quick Checkout component to add the following code in the SRC/components/Checkout. Vue:

<template>
  <Card title="Checkout Items" key="checkout">
    <p v-for="(k, i) in this.shoppingList" :key="i">
      Item: {{items[Number(k)].msg}} for ${{items[Number(k)].price}}
    </p>
  </Card>
</template>

<script>
import { Card } from 'ant-design-vue';

export default {
  props: ['shoppingList'],
  components: {
    Card
  },
  data: () => ({
    items: [
      { msg: 'First Product', price: 9.99}, {MSG:'Second Product', price: 19.99}, {MSG:'Third Product', price: 15.00}, {MSG:'Fancy Shirt', price: 137.00}, {MSG:'More Fancy', price: 109.99}, {MSG:'Extreme', price: 3.00}, {MSG:'Super Shirt', price: 109.99}, {MSG:'Epic Shirt', price: 3.00},]})} </script>Copy the code

Here, we will receive a shoppingList and print it to the screen.

You can run the application using the NPM run serve command. Then navigate to http:// localhost: 8080. If all goes according to plan, you should see something like the image below.

Try opening the Network TAB and clicking the Checkout button to see that the Checkout component will load asynchronously in the network.

You can also view the code for this demo on GitHub.

Add load and load error components for asynchronous components

Sometimes asynchronous components load too long or when loading. Displaying loading animations or error messages can be useful, but support this again slows down the application. Asynchronous components should be small and fast to load. Here’s an example:

const Message = () => ({
  component: import("./Message"),
  loading: LoadingAnimation,
  error: ErrorComponent
});
Copy the code

conclusion

Creating and implementing asynchronous components is simple enough that it should be part of standard development routines. From a user experience perspective, it is important to keep the initial load time as low as possible to keep the user’s attention. Hopefully, this tutorial has helped you build asynchronous load components.