Vue's question about keep-alive

my route is designed as two large routes, and then have child routes, similar to

routes:[
    {
        path:"/home",
        child:[
        {
            path:"/home/a",
        }
        {
            path:"/home/b",
        }
        ]
    },
    {
        path:"/other",
        child:[
        {
            path:"/home/c",
        }
        {
            path:"/home/d",
        }
        ]
    }
]

then I set

in app.vue
<keep-alive>
      <router-view></router-view>
      </keep-alive>

found that keep-alive does not take effect
but if I set keep-alive,a page to b page on the root component of two major routes, c page to d page is cached successfully, but a page to c page is not cached successfully, which Taoist friend encountered a similar situation?

Mar.07,2021

as mentioned above, < keep-alive > is only valid for direct subcomponents.
to make the cache from a to c valid, please refer to here .

in a nutshell, it means that < router-view > < router-view > is put into the same nested child < keep-alive >

for this < router-view > .

officially:
Note that < keep-alive > is used when one of its immediate subcomponents is turned on and off. If you have v-for in it, it won't work.

pay attention to whether there is a v-for


the cause of this problem is the nesting of multi-level routes.
specific analysis: if a background management system, there is a main.vue is the framework of all pages, there is such a code

          <keep-alive>
            <router-view v-if="$route.meta.keepAlive"></router-view>
          </keep-alive>
          <router-view v-if="!$route.meta.keepAlive"></router-view>

then create a parent menu, and the code in the ChildView.vue, page is as follows

<router-view></router-view>

suppose there are two submenus in this parent menu, distributed as An and B, and then An is set to cache (keepAlive=true), B does not cache (keepAlive=false);
when menu An is clicked, the system caches the ChildView.vue component.
when you click menu B, the ChildView.vue component is destroyed because the B setting is not cached.
this is the root cause of keep-alive invalidation.

there is something more complicated, such as a submenu in a menu can be cached, but clicking on another parent menu or a submenu under the parent menu is not cached, the principle is the same as the above analysis. is a multi-level menu, multiple shared components lead to keepAlive cache invalidation , keepAlive does not take into account the complexity of page cache at all.

analyze the problem:
since it is caused by multiple router-view nesting and sharing, if there is only one router-view, that is, you only need main.vue as a container for all pages in the framework, you will not have this problem.

in fact, whether the multi-level menu has no effect on the project or business at all, it is just needed in the interface display, so that users can click to the functional page they need more quickly. In this case, the displayed menu remains multi-level, and the actual router is divided into one level, separating the display menu from the business router.

solve the problem:
first cache the configured multi-level router with vuex and use it as a menu for display.
then convert the router into a first-level menu and add it asynchronously to the router with addRoutes.

partial sample code:

const formatRouter = (routes, newRoutes = []) => {
  routes.map(item => {
    if (item.children && item.children.length > 0) formatRouter(item.children, newRoutes);
    newRoutes.push(item);
  })
  return newRoutes;
}
let flatRoutes = formatRouter(routes);
router.addRoutes(flatRoutes);

then the breadcrumb navigation needs to be adjusted, and most of the logic is obtained from route.matched, but now that router is all first-level, we need to take the breadcrumb navigation data from the menu data displayed.

sample code:

/**
 * , ()
 * @param {*} val     
 * @param {*} data    
 * @param {*} fKey    
 */
export const recursiveTreeByLastLevel = (val, data, fKey = 'value') => {
  let rData = [];
  for (let i = 0, len = data.length; i < len; iPP) {
    rData.push(data[i]);
    if (data[i].children && data[i].children.length > 0) {
      rData = rData.concat(recursiveTreeByLastLevel(val, data[i].children, fKey));
      if (rData.some(item => item[fKey] === val)) return rData;
    }
    if (data[i][fKey] === val) return rData;
    rData = [];
  }
  return rData;
}
 
router.afterEach((to, from, next) => {
  var routerList = recursiveTreeByLastLevel(to.name, store.state.sidebarMenu, 'name')
  store.commit('setCrumbList', routerList) // vuex
})

finish it, call it a day!

Menu