Title: keep-alive: component-level cache tags:

  • keep-alive
  • Vue
  • vue-router

Page caching

In a VUe-built single page application (SPA), the routing module generally uses vuE-Router. Vue-router does not store the state of the switched component. When it pushes or replaces, the old component is destroyed and the new component is created, completing the entire lifecycle.

Sometimes, however, we need to keep the scroll bar of the list page at the same depth when we jump to the details page and return to the same position, which can improve the user experience. In Vue, this need for “page caching” can be addressed using the Keep-alive component.

use

Keep-alive is an abstract component (or functional component) that is not actually rendered in the DOM tree. It caches components in memory (without destroying them), keeps all of their states until the next rendering, and triggers the Activated hook function. Because the need for caching usually occurs during page transitions, it is often associated with router-view:

<keep-alive>
	<router-view />
</keep-alive>
Copy the code

This way, every component rendered in the Router-view will be cached.

If you want to render only certain pages/components, you can use the include/exclude attribute of the Keep-alive component. The include attribute represents the component name to cache (that is, the name attribute when the component is defined) and receives a string, RegExp, or String array; The exclude attribute has the opposite effect. Matched components are not cached. If N pages may appear in the same router-view and I only want to cache the list and details pages, I can write:

<keep-alive :include="['ListView', 'DetailView']">
  <router-view />
</keep-alive>
Copy the code

Implement conditional cache: global include array

Include is not commonly used, because it determines which pages are cached or not, given the following scenario:

  • Existing pages: home page (A), list page (B), details page (C), generally can be from: A->B->C;
  • When B goes to C and back to B, B keeps the list scrolling distance;
  • When B returns to A and enters B, B doesn’t have to stay in the state, it’s brand new.

Obviously, in this example, B is “conditionally cached”, keeping the cache when C->B and dropping the cache when A->B. The solution is simply to dynamically add/remove B from the include array. The specific steps are:

  1. Define a global cache array in Vuex to pass to include:
// global.js

export default {
  namespaced: true.state: {
    keepAliveComponents: [] // Cache the array
  },
  mutations: {
    keepAlive (state, component) {
      // Note: Prevent repeated additions (you can also use Set)! state.keepAliveComponents.includes(component) && state.keepAliveComponents.push(component) }, noKeepAlive (state, component) {constindex = state.keepAliveComponents.indexOf(component) index ! = =- 1 &&
        state.keepAliveComponents.splice(index, 1)}}}Copy the code
  1. Define keep-alive in the parent page and pass in the global cache array:
// App.vue <div class="app"> <! <keep-alive :include="keepAliveComponents"> <router-view></router-view> </keep-alive> </div> export default { computed: { ... mapState({ keepAliveComponents: state => state.global.keepAliveComponents }) } }Copy the code
  1. Cache: In the routing configuration page, the convention uses the meta property keepAlive, whose value is true to indicate that the component needs to be cached. This property is processed in the global routing hook beforeEach so that it is cached every time the component is entered:
const router = new Router({
  routes: [{path: '/A/B'.name: 'B'.component: B,
      meta: {
        title: 'B page'.keepAlive: true // The cacheability of component B is specified here
      }
    }
  ]
})

router.beforeEach((to, from, next) = > {
  // In routing global hook beforeEach, uniformly set the page caching according to the keepAlive property
  // Cache this component every time you enter it
  if (to.meta.keepAlive) {
    store.commit('global/keepAlive', to.name)
  }
})
Copy the code
  1. Timing of de-caching: The component-layer hook beforeRouteLeave that uses routing for caching components. Because B->A->B does not need to cache B, it can be considered that: if the next page of B is not C, then B will be brand new the next time we enter B component:
export default {
  name: 'B',
  created () {
      / /... Set the scroll bar at the very top
  },
  beforeRouteLeave (to, from, next) {
    // If the next page is not the details page (C), de-cache the list page (B)
    if(to.name ! = ='C') {
        this.$store.commit('global/noKeepAlive'.from.name)
    }
    next()
  }
}
Copy the code

Because B’s conditional cache is B’s responsibility, it is best to write the business logic inside B rather than in A so as not to confuse jump relationships between components.

  1. One detail to note: Because the include array of the Keep-Alive component operates on the component name, not the route name, we need to explicitly declare the name attribute for each component we define, otherwise the cache won’t work. Also, an explicit name hints at Vue DevTools.

Another way?

Another way to implement conditional caching seen online is very similar, but in the parent component, using two router-Views and conditional rendering:

// App.vue <div class="app"> <keep-alive> <router-view v-if="$route.meta.keepAlive"></router-view> </keep-alive> <router-view v-if="! $route.meta.keepAlive"></router-view> </div>Copy the code

There are some disputes in the GitHub issue (see the reference link), and I haven’t fully verified it, so I don’t know the effect yet.

Q&A

Q: The component name is already included in the global include array. Why is the page still not cached?

A: There is no problem if you follow the above thread, but note # 5: It is possible that you forgot to declare A name attribute to the component.

Q: Components can be cached, but scrollbars are not cached, e.g. will they return to the top?

A: The scrollbar issue has to do with the HTML structure of the component. At a glance, keep-alive caches the parent element’s scrollTop relative to the component, so if your component/page sets height:100% and the scrollTop is inside the component, you will see no cached scrollTop. Of course, about this, still need to enter the source in-depth, leave a pit for yourself.

reference

  • The document

Use -keep-alive on dynamic components

API: keep alive

  • application

Vue – keep alive – of the router

Vue refreshes forward and refreshes backward

  • discuss

https://github.com/vuejs/vue-router/issues/811