Yes, this is how to cache pages by url with Vue, Vue Router and Keep Alive

Futari Boy - developer & indie hacker
ITNEXT
Published in
4 min readApr 2, 2019

--

Back in the days, Ionic 1 was for me the best hybrid mobile framework.
It was for 2 reasons:

  • The UI and the built-in components were amazing and full-featured
  • The cache-per-route system on the router was a time-saver

When you create mobile experiences, you know that bandwdith is a real issue, therefore, you don’t want your user to be seeing loaders all the time. You want to do as less API calls as possible.

When your user has seen a page, navigates out and comes back, you don’t want to reload the content most of the time, as the content may not have changed during your user’s session.

On the Facebook app, each view is cached most of the time.

Unfortunately, I didn’t find any built-in cache system in Vue Router.

Let me break it down in detail:

  • I have a component to show an Anime item
  • I link this component to a router with the path: /anime/:id
  • In the component, I make an API call by ID to show content
  • If I go to /anime/1, component is created and rendered
  • If I go to /home, then back to /anime/1,
    the component is re-rendered, no cache

So can we fix this? Yeah, I see you coming:

You can cache with the keep-alive component

That is only partially true. Let’s see how keep-alive works.

The keep-alive component caches the whole component, and not the component rendered depending on its URL.
Why? Because keep-alive is not tied to Vue Router, it’s a built-in component in Vue itself so, it has no direct link with the router’s state.

keep-alive creates the following behaviour:

  • I go to /anime/1, component is created, rendered and kept alive
  • I go to /home, then back to /anime/3,
    the page will show the content of /anime/1

Make sure to always go to /home before going to an anime URL, because despite the use of keep-alive, when Vue Router goes from /anime/1 to /anime/2, it isn’t changing component, it only changes the URL. To patch this, you would need to watch the $route property and update your component accordingly, but this is not necessary in this example. If you want to dig it though: click here.

Example WITHOUT keep-alive: see the example.
Example WITH keep-alive: see the example.

As you can see, the keep-alive component creates cache, but now all urls /anime/:id will show the content of the first cached anime page.

How can we avoid this? By using a key prop on the router-view.

Example with the key prop: see the example.

Why does this fix all our problems? To fully understand why, you need to understand what the key prop does in Vue and how router-view works.

The key prop is used to differentiate same elements in the Virtual DOM.

router-view is a sophisticated version of <component is=… />.
Knowing that, when you go from /a to /b, componentA and componentB are switched and one replaces the other at the same place in the Virtual DOM.
That’s why when you move from /anime/1 to /anime/2 the router does not destroy the component to re-create it, because for Vue, both use the same component so there’s no need to destroy it and re-create it.

What we did by adding a key prop to the router-view, is basically saying:

Consider that each router-view per key is a different component

Then, thanks to our keep-alive component, it will cache each component by key, since each key is a URL in our case, it will cache by URL :)

Let me break it down:

  • On /home, the keep-alive caches <router-view key=”/home” />
    which evaluates to Home component <home key=”/home” />
  • On /anime/1, it caches <router-view key=”/anime/1” />
    which evaluates to Anime component <anime key=”/anime/1” />
  • On /anime/5, it caches <router-view key=”/anime/5” />
    which evaluates to Anime component <anime key=”/anime/5” />
  • When back on /anime/1, it shows the cache tied to key=”/anime/1”

Here, keep-alive thinks <anime key=”/anime/1” /> and
<anime key=”/anime/5” /> are “not the same component” because with the key prop we are explicitly saying “they are different”.

In this scenario, you can even directly go from /anime/1 to /anime/5 because now, each URL will force the re-render the frist time but will be cached for the next rendering.

Summary

  • Add keep-alive around router-view to cache each page component
  • Add a key prop with the URL as a value on router-view to tell Vue to force re-render if the component is the same but the key is different

That’s all you need and now you have each page cached (in-memory) by URL. https://jsfiddle.net/darkylmnx/ey9acx1z/31

Use this wisely as it’s for specific use cases and that you’ll need to handle cache-invalidation when needed.

BONUS 1: You can use the max prop on the keep-alive component to limit the number of cached items. That would invalidate cache easily for you. https://jsfiddle.net/darkylmnx/ey9acx1z/32/. Read more about the max prop here.

BONUS 2: Cache invalidation really depends on your business logic but let me give you a simple example. You could use the instance hooks activated and deactivated to check when the user enters a cached component (since created and mounted won’t be called) and based on a counter or some expiration date re do an API call, see the example.

Want to discover fresh music? Checkout my projet https://foreignrap.com

PS: If you want to learn how to create advanced components, checkout my course: https://courses.maisonfutari.com/mastering-vue-components-creating-a-ui-library-from-scratch?coupon=MEDIUM

There’s a 50% discount because you’re coming from this story.

KEEP ALIVE

--

--

Developer, nomad, SaaS & micro-SaaS founder 👋 #indiehacker 👨‍💻 #freelance 🇯🇵 #anime #japan