{{announcement.body}}
{{announcement.title}}

Speed Up Your Vue.js Single-Page App

DZone 's Guide to

Speed Up Your Vue.js Single-Page App

Increase the performance of Vue.js applications by focusing on lazy-load components and routes and functional components.

· Web Dev Zone ·
Free Resource

One of my projects included building a Single Page Application with Vue.js. As the Go Live approached, the topic of performance optimization became more relevant. In this article, I have collected everything I have learned about improving the performance of Vue.js applications with regards to loading times and rendering performance.

With Vue.js, you are able to quickly build a Single Page Application. Webpack will bundle everything into files (HTML, JavaScript, CSS) for you, and finally, you can serve it with nginx. At least, this is our setup. However, Webpack will warn you that some assets have gotten too big.

Webpack warning for an excessively large entry-point bundle

It is important to note that these three files will be loaded once the user visits the SPA and only afterward will the page is rendered. However, most of the files contents is not even needed for the initially loaded page and should not block the user from using our website as quickly as possible.

The following article presents several approaches on how you can mitigate this issue and further approaches to improve your Vue.js application in terms of responsiveness and performance.

Functional Components

Functional components are components that hold no state and no instance. Turning your stateless Vue component into a functional component can drastically increase your rendering performance. 

Simply add the functional keyword to the top-level template tag:

HTML


To access your props and data as before, you have to make some minor adjustments.

HTML
 




xxxxxxxxxx
1
10


 
1
<template functional>  
2
  <div>{{ props.someProp }}</div> 
3
</template> 
4
<script> 
5
  export default {  
6
    props: {    
7
      someProp: String  
8
    } 
9
  }
10
</script>



In case you are using i18n for internalization, you have to prepend parent to $t:

JavaScript
 




xxxxxxxxxx
1


 
1
{{ parent.$t('app.not-found.message') }}



With functional components, we do not have access to methods or computed props. However, we can still access methods using $options.

HTML
 




xxxxxxxxxx
1
10


 
1
<template functional>  
2
  <div>
3
    {{ $options.username(props.user) }}
4
  </div> 
5
</template> 
6
<script> 
7
  export default {  
8
    props: {    
9
      user: User,  
10
    },   
11
    username(user: User): string {    
12
        return user.name;  
13
    } 
14
  }
15
</script>



Lazy-Load Components

Lazy loading components can save a lot of time on initial download. Any lazily loaded resource is downloaded when it’s import() function is invoked. In the case of Vue components that happens only when it is requested to render. Dialogues are predestined for this. They are usually shown only after user interaction.

HTML
 




xxxxxxxxxx
1
10


1
<template> 
2
  <div>     
3
    ...    
4
    <app-modal-dialog v-if="showDialog" />  
5
  </div> 
6
</template> 
7
<script> 
8
  export default {  
9
    components: {    
10
      ModalDialog: () => import('./ModalDialog.vue')  
11
    } 
12
  }
13
</script>



Webpack will create a separate chunk for the ModalDialog component, which will not be downloaded immediately on page load but only once it is needed.

Be careful not to lazy-load a component that should be shown automatically. For example, the following would (silently) fail to load the modal dialog.

JavaScript
 




xxxxxxxxxx
1


 
1
mounted() {    
2
  this.$bvModal.show('password-check'); 
3
},



The reason is that the mounted hook is evaluated before the modal component is lazy loaded.

Lazy-Load Routes

When building SPAs, the JavaScript bundle can become quite large, and thus increase the page load time. It would be more efficient if we can split each route’s components into a separate chunk and only load them when the route is visited.

JavaScript
 




xxxxxxxxxx
1


 
1
import ProjectList from '@/components/ProjectList.vue'; 
2
export const routes = [  
3
  {    
4
    path: '/projects',    
5
    name: 'projects',    
6
    component: ProjectList,  
7
  }, 
8
]



It is very easy to define an async component that will be automatically code-split by Webpack. Just change the import statement:

JavaScript
 




xxxxxxxxxx
1


1
const ProjectList = () => import('@/components/ProjectList.vue');



Apart from that, nothing needs to change in the route configuration. Build your app in production mode with:

JSON
 




xxxxxxxxxx
1


 
1
"build": "vue-cli-service build --mode production"



and verify that a lot of chunks will be generated

Code-Splitting in Vue and Webpack

You can also verify that this is working by opening the developer console in your browser. In the Network tab, several JavaScript files will be loaded asynchronously once you visit a new route. In develop mode, each chunk will be given an auto-incremented number. In production mode, an automatically calculated hash value will be used instead.

Lazy-Loaded Chunks and Prefetch Cache

A very cool feature is that Vue automatically adds magic comments for Webpack so that further chunks will be prefetched automatically (see prefetch cache). However, prefetching will only start after the browser has finished the initial load and becomes idle.

Make Object Lists Immutable

Usually, we will fetch a list of objects from a backend, for example users, items, articles, etc. By default, Vue makes every first-level property for each object in the array reactive. That can be expensive for large arrays of objects. Sometimes, we do not need to modify them when we just want to display objects. 

So, in these cases, we can gain some performance if we prevent Vue from making the list reactive. We can do that by using Object.freeze on the list, e.g. making it immutable.

JavaScript
 




xxxxxxxxxx
1


 
1
export async function get(url: string): Promise<User[]> {   
2
  const response = await Object.freeze(axios.get<User[]>(url));   
3
  return response.data; 
4
}



Measure Runtime Performance

We have talked about quite a few ways to improve a Vue SPA, but we do not know how much performance we actually have gained. We can do so by using the Performance tab in our browser’s Developer Tools. 

In order to have accurate data, we have to activate the performance mode in our Vue app. Let us activate it for developer mode in our main.ts file with

JavaScript
 




xxxxxxxxxx
1


 
1
Vue.config.performance = process.env.NODE_ENV !== "production";



This activates the User Timing API that Vue uses internally to measure component performance. 

Open your browser and press F12 to open the Developer Console. Switch to the Performance tab and click Start Profiling. In Chrome, the Timings row shows important marks, such as the Time of First Contentful Paint and First Meaningful Paint. These are measures you should try to decrease so that your users can use the website as fast as possible.

Do you have more tips and tricks to improve the performance of Vue.js applications? Let me know in the comments!

In this part, we have seen how we can use lazy-loading for routes and components to split our SPA into chunks, how functional components can improve the performance, and how we can measure these improvements.

Topics:
performance, single page apps, tutorial, vue js, web design and web development, webpack

Published at DZone with permission of Dr. Matthias Sommer . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}