Author: Binggg from thunder front end

Vue Application Performance Optimization Guide

Thanks to Vue’s responsive system and virtual DOM system, Vue can automatically track the dependency of data in the process of rendering components, and accurately know which component needs to be re-rendered when the data is updated. After rendering, it will go through the virtual DOM diff before it is really updated to the DOM. Developers of Vue applications generally do not need to do additional optimization work.

However, performance problems can still be encountered in practice. Here are some ways to locate and analyze Vue application performance problems and some optimization suggestions.

The overall content consists of three parts:

  • How do I locate Vue application performance problems
  • Vue Runtime performance optimization suggestions
  • Vue Indicates suggestions for optimizing application loading performance

1. How do I troubleshoot Vue application performance problems

The performance problem of Vue applications can be divided into two parts, the first part is the runtime performance problem and the second part is the load performance problem.

As with other Web applications, the best tool for locating Performance problems in Vue applications is Chrome Devtool. The Performance tool can be used to record runtime Performance problems such as CPU usage, memory usage, FPS, etc. over a period of time. The Network tool can be used to analyze load performance problems.

For example, using the Bottom Up TAB of the Performance tool, we can see which operations are the most expensive over a period of time. This is useful for optimizing CPU usage and low FPS. We can see where the most expensive operations are taking place. We can do some specific optimization.

For more details about how to use Chrome Devtool, see the guide to using Chrome Devtool to locate performance problems

2. Vue runtime performance optimization suggestions

The runtime performance of Vue applications is mainly concerned with the usage of RESOURCES such as CPU, memory, and local storage after initialization, and the timely response to user interactions. Here are some useful optimizations:

2.1 Importing Vue files into the production environment

In the development environment, Vue provides many warnings to help you deal with common errors and pitfalls. In a production environment, these warnings are useless and increase the size of the application. Some of the warning checks also have minor runtime overhead.

When using a build tool like Webpack or Browserify, the Vue source code uses process.env.node_env to decide whether to enable production environment mode, which is the default. There are methods in both Webpack and Browserify to override this variable to enable Vue’s production environment mode, and warnings are removed by the compression tool during the build process.

See Production Deployment for details

2.2 Precompile templates using single-file components

When using an in-DOM template or a string template within JavaScript, the template is compiled into a rendering function at run time. This process is usually fast enough, but it’s best to avoid it for performance-sensitive applications.

The easiest way to precompile a template is to use a single-file component — the relevant build Settings take care of the precompile automatically, so the built code already contains the compiled render function rather than the original template string.

See Precompiled Templates for details

2.3 Extract components from the CSS into separate files

When using a single-file component, the CSS within the component is injected dynamically via JavaScript in the form of

Check out the respective documentation for this build tool to learn more:

  • webpack + vue-loader (vue-cliWebpack templates are pre-configured.)
  • Browserify + vueify
  • Rollup + rollup-plugin-vue

2.4 usingObject.freeze()Improve performance

Object.freeze() freezes an Object. You cannot add new properties to the Object, change the value of existing properties, delete existing properties, or modify the enumerability, configurability, or writability of existing properties of the Object. This method returns the frozen object.

When you pass a normal JavaScript Object to the Data option of a Vue instance, Vue will walk through all the properties of the Object and use object.defineProperty to turn them into getters/setters. These getters/setters are invisible to the user, but internally they let Vue track dependencies and notify changes when properties are accessed and modified.

However, Vue does not add data hijacking methods such as setters and getters to objects that have been set to non-configurable properties, such as object.freeze (). Refer to Vue source code

Vue observer source

2.4.1 Performance Improvement Comparison

In Vue’s Big Table Benchmark, you can see the comparison between rendering a 1000 x 10 table and turning on object.freeze ().

big table benchmark

Before turning on optimization

With optimization turned on

In this example, using object.freeze () was 4 times faster than not using it

Why 2.4.2Object.freeze()Performance will be better

Do not useObject.freeze()The CPU overhead

useObject.freeze()The CPU overhead

As you can see by comparison, the use of object.freeze () reduces the overhead of the observer.

2.4.3 Object.freeze()Application scenarios

Since object.freeze () freezes the Object, it’s a good idea to show the class. If your data properties need to change, you can replace them with a new object.freeze () Object.

2.5 Flat Store Data Structure

In many cases, we will find that the information returned by an interface is in a deeply nested tree structure like this:

{
  "id": "123"."author": {
    "id": "1"."name": "Paul"
  },
  "title": "My awesome blog post"."comments": [{"id": "324"."commenter": {
        "id": "2"."name": "Nicole"}}}]Copy the code

If direct store this structure in the store, if you want to modify one commenter information, we need to traverse the layers to find the user’s information, and may the user information appeared many times, you also need to put the rest of the user information is modified, each traversal process will bring additional performance overhead.

If we store user information in a unified structure such as Users [ID] in the store, the cost of modifying and reading user information becomes very low.

You can manually store the information in the interface as a table of data like this, or you can use some tools. This is a concept called JSON normalize. Normalizr is an open source tool. You can convert the above deeply nested JSON objects through a defined schema into an entity representation using an ID as a dictionary.

For example, for the JSON data above, we define three schemas for Users Comments Articles:

import {normalize, schema} from 'normalizr';

// Define the Users Schema
const user = new schema.Entity('users');

// Define the comments schema
const comment = new schema.Entity('comments', {
  commenter: user,
});

// Define articles schema
const article = new schema.Entity('articles', {
  author: user,
  comments: [comment],
});

const normalizedData = normalize(originalData, article);
Copy the code

After normalize, we can obtain the following data, which can be stored in the store in this form, and then modify and read the user information of a certain ID becomes very efficient, with the time complexity reduced to O(1).

{
  result: "123".entities: {
    "articles": {
      "123": {
        id: "123".author: "1".title: "My awesome blog post".comments: [ "324"]}},"users": {
      "1": { "id": "1"."name": "Paul" },
      "2": { "id": "2"."name": "Nicole"}},"comments": {
      "324": { id: "324"."commenter": "2"}}}}Copy the code

For more information please refer to the normalizr documentation github.com/paularmstro…

2.6 Avoid performance problems caused by persistent Store data

When you need to keep the Vue App available offline, or for disaster recovery if there is an interface error, you may choose to persist the Store data. Here are a few things to consider:

2.6.1 Write performance problems during persistence

The vuex-PersistedState, which is popular in the Vue community, uses the store subscribe mechanism to subscribe to store data, and if there is a change, it is written to the storage. By default, localstorage is used as persistent storage.

This means that by default each commit writes data to localstorage. Localstorage writes are synchronous and have a significant performance overhead. If you want to build 60FPS applications, you must avoid writing persistent data frequently

Here is a screenshot from the Performance tool in the development environment, and you can see a 6s lag:

Six seconds of caton

Bottom-up shows that setState takes 3241.4ms of CPU execution time, and that setState is writing data to the Storage.

Vuex – persistedstate setState source code

We should try to minimize the frequency of writing directly to Storage:

  • Multiple writes are combined into one, such as using function throttling or caching the data in memory and then writing it together
  • Write only when necessary, such as when the data of the module of interest changes

2.6.2 Prevent persistent storage capacity from growing

Due to the limited capacity of persistent cache, such as the cache of localstorage is only 5M in some browsers, we cannot store all the data without limit. It is easy to reach the capacity limit. At the same time, when the data is too large, read and write operations will add some performance overhead, and memory will also increase.

In particular, after normalizing API data and flattening it, one copy of data will be scattered on different entities, and the next request for new data will be scattered on other different entities, which will lead to continuous storage growth.

Therefore, when designing a persistent data cache strategy, you should also design a cache clearance strategy for old data, such as clearing old entities one by one when new data is requested.

2.7 Optimizing the performance of the infinite list

If your application has very long or infinitely scrolling lists, use windowing techniques to optimize performance by rendering only a small area of content, reducing the time needed to re-render components and create DOM nodes.

Vue-virtual-scl-list and Vue-virtual-Scroller are both open source projects that address these issues. You can also try your own implementation of a virtual Scroller list to optimize performance by referring to The Google Engineers article Complexities of an Infinite Scroller, using techniques such as DOM recycling, tombstone elements, and scrollanchor.

Infinite list design drawn by Google engineers

2.8 Optimize the initial rendering performance of excessively long application content through lazy component loading

Mentioned above list of infinite scene, suit to list elements are very similar, but sometimes, your long list of the Vue applications within the content often is not the same, for example in a complex, in the main interface of the application of the main interface is composed of so many different modules, and only the first screen users see one or two modules. Modules that are not visible at the time of the initial render will also be executed and rendered, introducing some additional performance overhead.

Using component lazy loading only renders a skeleton screen when not visible, without actually rendering the component

You can lazily load components directly and avoid the overhead of initializing the render runtime by not loading and initializing components in invisible areas. See our previous column performance Optimization for Component Lazy Loading: Vue Lazy Component Introduction for more information on how to do component-granularity Lazy loading.

3. Optimize Vue application loading performance

3.1 Use server side rendering (SSR) and Prerender to optimize load performance

In a single-page application, there is often only one HTML file, and then the content of the page is dynamically rendered by matching the routing script based on the URL accessed. A big problem with single-page apps is that the first screen is too long.

Single-page applications make multiple requests to display a page, first getting the HTML resources, then using the request to get the data, and then rendering the data to the page. And because of today’s microservice architecture, it is also possible to make multiple data requests to render a web page, and each data request generates RTT (round-trip latency), resulting in a long delay in loading the page.

Comparison of server rendering, pre-rendering, and client rendering

In this case, server rendering (SSR) and Prerender can be used to improve load performance. In both scenarios, the user reads the web content directly, which saves a lot of RTT (round-trip latency). In addition, some resources can be inlining the page, which can further improve load performance.

For more information on how to optimize with pre-render, see our optimization Direction: A Single Page Application guide to Multi-Route Pre-render.

Server rendering (SSR) can be built using Nuxt or follow the Vue SSR guide provided by Vue.

3.2 Optimizing the Loading performance of excessively long Application content through Lazy Component Loading

In the long application scenario mentioned above, the component lazy loading scheme can be used to optimize the performance of the initial render. In fact, it also helps to optimize the loading performance of the application.

Component-granularity lazy loading combined with asynchronous components and Webpack code fragmentation can ensure on-demand loading of components, as well as the resources that components depend on, interface requests, etc. Compared with ordinary lazy loading of images, it further achieves on-demand loading of resources.

Use the request waterfall diagram before the component is lazily loaded

Use the request waterfall diagram after the component is lazily loaded

Using a Component Lazy load scheme is a great way to initialize renderings for applications with very long content, reducing the number of necessary resource requests and shortening the critical rendering path. For details, see our previous column: Component Lazy Loading for performance Optimization: Introduction to Vue Lazy Component.

conclusion

This article summarizes some of the performance optimization measures for Vue applications at runtime and load time. Here is a review and summary:

  • Vue application runtime performance optimization measures

    • Import the Vue file for the production environment
    • Precompile templates using single-file components
    • Extract the component’s CSS into a separate file
    • usingObject.freeze()Improve performance
    • Flatten the Store data structure
    • Use persistent Store data wisely
    • Component lazy loading
  • Vue Measures for optimizing application loading performance

    • Server render/pre-render
    • Component lazy loading

Of course, the performance optimization methods summarized in this article do not cover all Vue application performance problems. We will continue to summarize and supplement other problems and optimization measures. I hope that the practical experience mentioned in this article can help you in your Vue application performance optimization work.