vue routing cache ultimate solution

Posted by adders on Sun, 26 Sep 2021 02:48:24 +0200

preface

There is often such a demand in the business. When the page jumps, the state and data of the previous page are maintained to facilitate processing when returning
For example, a - > B needs caching, and a - > C does not need caching
Most online examples need to deal with business components, which is very unreasonable
Therefore, the shared modification function is used to deal with such problems

The keepAlive component of vue is very suitable for solving such problems, but it still has some defects. For example, it is difficult to flexibly control whether caching is needed according to the origin. It can only simply match whether caching is needed through include

The requirements after sorting are as follows:

1. The component can cache on some pages, and if it is separated from these pages, the cache will be cleaned up
2. The component can be cached globally
3. It's best to solve it from the architecture level without interfering with business components

General idea

Add keealive: ['name of route'] in the route meta to determine the effective action page
Set a store to bind keepalive
Process the store in the global routing hook

The code is as follows

keepAlive is set in the routing meta, which means that caching components are required to access these pages

// Routing configuration
{
    path: 'xxx',
    name: 'xxx',
    component: () => import('xxx'),
    meta: {
        title: 'xxx',
        keepAlive: ['route name', 'b'],//Scope: cache on these pages
    },
},

Set keepAliveIncludes in the store to bind keepAlive  

// vuex setting and deleting cache components
state: {
    keepAliveIncludes:[]
 },
mutations: {
    // Set cache
    SET_KEEPALIVEINCLUDES: (state, data) => {
      const has = state.keepAliveIncludes.find(i=>i.path === data.path)
      if(!has){
        state.keepAliveIncludes.push(data)
      }
    },
    // Delete cache
    DELETE_KEEPALIVEINCLUDES:(state, data) => {
      state.keepAliveIncludes = state.keepAliveIncludes.filter(i=>i.always)
    }
  },
// Business page, using keepAlive components
<template>
  <keep-alive :include="keepAliveIncludes" >
    <router-view ref="content"/>
  </keep-alive>
</template>

<script>
export default {
  computed: {
    'keepAliveIncludes'() {
      const arr = this.$store.state.keepAliveIncludes.map(i=>i.componentName)
      return Array.from(new Set(arr))
    },
  }
</script>

This will bind keepAliveIncludes to keepalive components
Because the keepAlive component internally subscribes to include, we don't need to consider how to delete the existing cache. We just need to handle the value keepAliveIncludes, which will be deleted by the component internally

Handle keepAliveIncludes through the global routing hook

// Routing hook
router.beforeEach((to, from, next) => {
    // Take out the keepalive of all cached pages and flatten them
    let cachePath = store.state.keepAliveIncludes.map(i => i.keepAlive).flat(Infinity)
    // Clean up all non resident cache components when you go to a page that is not cached
    if (!cachePath.includes(to.name)) {
        // console.log('pages not cached ');
        store.commit('DELETE_KEEPALIVEINCLUDES')
    }

    if (to.meta.keepAlive) {
        // const componentsName = to.matched[to.matched.length-1].components.default.name
        store.commit('SET_KEEPALIVEINCLUDES', {
            path: `${to.name}`,
            componentName: to.name, //The route name is required to be consistent with the name of the default component
            always: to.meta.keepAlive.length === 0,
            keepAlive: to.meta.keepAlive,
        })
    }
})

process

The first implementation idea is as follows:

The line determines whether it matches, and the matching caches the component
However, it was found that the method of keepAlive cache was executed before router.beforeEach and failed

Then I changed my mind

Cache components first, and then check whether they match. If not, delete the existing cache

In the middle, we also encountered the problem of keepAlive. There is no API for cached components on vue's official website, so we checked a lot of information on the Internet and found that keepAlive has subscribed to include. As long as the include changes, cached components that are not in the include will also be cleaned up

appendix

This feature is still defective
This method requires the name of the default routing component to be consistent with the name of the routing list
Because the value received by the include instruction of keepAlive is the component name, and we load the component asynchronously, the component has not been instantiated during router.beforeEach, so we can't get the component name
Of course, if the component is loaded synchronously, you can use the following code to get the name of the component

to.matched[to.matched.length-1].components.default.name

If a big man knows how to solve it, welcome to leave a message for discussion  

Topics: Javascript Vue.js Cache