Skip to content

Client-side caching patterns in Vue.js applications

Hi! I want to address today a question of local caching in Vue.js applications. This is an important topic, as by providing a local copy of data, we don’t need to call API everytime, increase an overall speed and improve user experience. Moreover, local caching is the must, when we need to store data, computed by Vue app itself. There are several options available, as well different architectural design patterns.

This post overviews implementations of three most common solutions using Axios (for server calls) and Localforage library for working with local stores. The last one is not the only one available; for instance Vue docs list other options, including vue-local-storage. However, it provides unified async API for working with different local storing options (local storage, indexed DB, webSQL), supported by Mozilla and can be used at any web application.

We will start from reviewing a definition of caching and reasons why do we need to deal with it. We also implement three patterns of client side caching.

What is caching and why to do it?

Before we will progress with concrete caching patterns for Vue.js web applications, it is important to review what do we understand as caching and what are reasons to bother with it. In this post by caching we define a storage of data locally, e.g. on user’s side. While many frontend developers do not even want to hear about caching, I suggest you to try it, as it brings a number of advantages to you, including (but not limited to):

  • Increase speed and improve user experience by having commonly requested data on local device, rather than fetching it everytime from API
  • Improve data avability – as data has a local copy, it can substitute, in cases when server is not available
  • Store computed data – when you need to save somewhere data, computed by SPA itself, you have to deal with storage

The simplest form of local data persistance is Local Storage API (if you are not familiar with it, follow this excellent tutorial by Tania Rascia). While it is pretty straightforward and available for you out-of-the-box, Local Storage may be not the best tool that fits all tasks. First of all, you need to understand that technically Local Storage saves all data as one big JavaScript object. Also it is synchronous and it can be really a bottleneck for big and complex applications. Finally, you need to remember that Local Storage requires you to serialize your objects to store them, as API supports only string data type. You can read more on LocalStorage issues in this post – while I am not 100% agree with the author, I place it here as I am open for other points of view.

Other options include IndexedDB or using libraries like Vuex. Also, they have an opposite disadvantage – they may be too complex for most apps. As my opinion, you can try to utilize LocalForage library as it abstracts a number of local storing options (including LocalStorage and IndexedDB) with unified async API. During this post we will overview how to implement several common caching patterns with LocalForage and Axios in your Vue.js applications.

Configure a project

In this post we will use two dependencies – Axios and Localforage. The first one is a common way for Vue.js to work with HTTP APIs and we make it responsible for fetching data from server. The second dependency – LocalForage – is used to abstract local caching options using unified async API. You could either install original library, or stick with Vue LocalForage driver. In this post I will utilize the second approach.

First, add dependencies to the project. To install Vue driver run this command in your terminal:

npm install --save vlf

Next step we need to tell Vue to use Vue driver, so it will be available via Vue instance as this.$vlf object. Add following code in main.js file:

import Vlf from 'vlf'

//...

Vue.use(Vlf)

Vue driver uses same API as Localforage, so you can call methods on this.$vlf like you will do with localforage, including configuration options using this.$vlf.config by passing config object (you can find available options in documentation).

Caching patterns in Vue.js applications

The concept of this post appeared after reading the post by Brock Reece on frontend caching strategies. So, I’d like to introduce you to implementations of principles, mentioned in the aforesaid article for Axios from one side for API fetching and LocalForage as local storage option.

Pattern A: API first

The most popular approach is to prefer API data over local storage. In this situation, we cache the last successful server response and revert to it, when API is not available (for instance, due to the network error). Such pattern is common in building offline first applications, but honestly does not provide a great improvement of performance. The code snippet below demonstrates its usage:

import axios from 'axios'

data() {
    return {
        books: []
    }
}, 
methods: {
    getEntities(){
        axios.get('http://localhost:4567/v1/books')
        .then(result=>{
            let data = result.data
            this.books = data
            this.$vlf.setItem('books', data).then(r => {
                console.log('Books saved locally')
            })
        })
        .catch(error=>{
            console.log(error)
            this.$vlf.getItem('books').then(data => {
                if (data!==null){
                    this.books = data
                }
            })
        })
    }
},
created() {
    this.getEntities()
}

Note, I abstracted logic inside getEntities method and isolated it from lifecycle hook. This pattern prefers server data over local persistance. We perform API call using Axios’s get method. After we successfully obtained data, we save it locally. In a case of errors we rollback to the last saved copy.

Pattern B: Local first

Contrary to the first solution, where we prefer server data and only revert to local cache in case of problems, this approach is local first. In this case we save data locally after the initial successful retrivement and then consume it from local cache, rather call API everytime. Such approach sets some limitations so it usually used for data that does not change often. As example from my own experience, I can recall VAT tax list in accounting apps – such list is different for each country (e.g. tax jurisdiction), although is not changed during whole session.

Let see how we can implement such pattern using Axios and LocalForage. Consider the following code snippet:

import axios from 'axios'


data() {
    return {
        vatRates: []
    }
}, 
methods: {
    getRatesFromAPI(){
        axios.get('http://localhost:4567/v1/public/vat-rates')
        .then(result=>{
            let rates = result.data
            this.vatRates = rates
            this.$vlf.setItem('vatRates', rates)
        })
    },
    getVatRates(){
        this.$vlf.getItem('vatRates').then(data=>{
            if (data!==null){
                this.vatRates = data
            } else {
                this.getRatesFromAPI()
            }
        }).catch(error=>{
            this.getRatesFromAPI()
        })
    }
},
created() {
    this.getVatRates()
}

Let review this code. In this example we store locally VAT rates, that is a data, which is not changed everytime, so we can cache it in local storage and retrieve it from there. On created hook we call getVatRates(). This method retrieve data from local cache. In case of error (or if data is not available – is null), we call Axios to get data from server and store it to get available next time from local storage.

Pattern C: API after local storage

The last option we will implement combines both approaches. NB that you can see it similar to the second one, but they are different. First, let take this code snippet:

import axios from 'axios'

data() {
    return {
        books: []
    }
},
methods: {
    getBooks(){
        this.$vlf.getItem('books').then(data => {
            this.books = data
            axios.get('http://localhost:4567/v1/books')
            .then(result => {
                this.books = result.data
                this.$vlf.setItem('books', result.data)
            })
        })
    }
},
created() {
    this.getBooks()
}

You can say that this approach prefers local data, like the second one, but it is just half truth. Look, that in the previous example we fetched data from API only when local copy is not available (as catch callback or in case when result from local cache is null). Here we get data first from local store, however then we perform API call to update local copy.

This pattern combines best of two previous approaches: it preloads data on created from local cache and then update it once Axios completed request.

Conclusion

Hope this post was useful for you. We observed what is client-side caching and how to achieve it in Vue.js applications using Axios and Localforage. Also we implemented three common design approaches, you can use in your projects. This does not limit your options: you can use Vuex, for example. Although, it can be fairly complex, not to name is a large dependency, and you can look on options, described here. Anyway, patterns are implementation-independent and can be utilized at any web application, not only in Vue.js ones.

References

  • Brock Reece Frontend caching strategies (2017) Medium, read here
  • Client-side storage VueJS documentation, read here
  • Kristopher Sandoval Balancing Client and Server Caching Web Application Development (2018) NORDICAPIS, read here
  • Matthias Hager Adding LocalStorage to our Vue.js Application (2018) read here
  • Pedro Teixeira Build More Reliable Web Apps with Offline-First Principles (2016) The New Stack, read here
Copy link
Powered by Social Snap