I’m participating in nuggets Creators Camp # 4, click here to learn more and learn together!

The cause of

Our company is the main toB business, often developed is the background management system. Since 19th, the front-end technology stack of most new projects has changed from JSP + Layui +jquery to Vue family bucket + elementui, and the new framework is based on vue-elemen-Admin-master secondary development. After more than a year of stable online operation, the previous client suddenly responded that it deleted a TAB page and then opened it again. It was still the cached value without re-initialization. I tested it for many times and found that the problem did not always reappear

What kind of question is that, black question mark

Found the problem

The bug was discovered by debugging tools and Vue DevTools bit by bit. Level-2 routes are normally cached, and the problem lies in routes above level-3.

The logic for caching is mainly in the AppMain component under Layout

The inside of thecachedViewsIt is maintained in store and records the name value of the route.

[‘b’, ‘c’, ‘d’] [‘b’, ‘c’, ‘d’] [‘b’, ‘c’, ‘d’] [‘b’, ‘c’, ‘d’] After the above two routing sequences are clicked, the structure seen in Computent in Vue Devtools should be

-Root
 -App
  -Layout
   -Navbar
   -Sidebar
   -TagsView
   -AppMain
    -b      [inactive]
      -c    [inactive]
    -b
      -d
    -keep-alive
Copy the code

[root@localhost] [root@localhost] [root@localhost] [root@localhost] [root@localhost] [root@localhost]

SRC /core/components/keep-alive.js SRC /core/components/keep-alive.js


function matches (pattern: string | RegExp | Array<string>, name: string) :boolean {
  if (Array.isArray(pattern)) {
    return pattern.indexOf(name) > -1
  } else if (typeof pattern === 'string') {
    return pattern.split(', ').indexOf(name) > -1
  } else if (isRegExp(pattern)) {
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}

// Delete the cache
function pruneCache (keepAliveInstance: any, filter: Function) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    constcachedNode: ? VNode = cache[key]if (cachedNode) {
      // The main problem is that name is always the name of the secondary routing component, so it cannot be deleted
      constname: ? string = getComponentName(cachedNode. componentOptions)// In this example, filter(name) is true, so there is no logic to delete cache
      if(name && ! filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } } } mounted () {// After initialization, listen for changes in the include to determine whether to delete the cache
  this.$watch('include'.val= > {
    pruneCache(this.name= > matches(val, name))
  })
}

Copy the code

When refreshing is clicked on the label, d is removed from the include array, but inactive cache D is not deleted because const name:? In the pruneCache after triggering include listening String = getComponentName ponentOptions (cachedNode.com) access to the name of b, because b still exist, can’t delete the corresponding cache d.

To solve the problem

Once a problem is identified, it is much easier to find a solution. So the final solution was chosen

  • Method one: refer to the method of a big guy (forgot to save website address after seeing). Since secondary routes do not have this problem, the data is changed to secondary routes. The menu uses the original multi-level data and the data by addRoute is changed to secondary routes.
// Use const nestedRouter = {path: '/test', Component: Layout, redirect: '/test/ menU1 / menU1-1 ', name: 'test', children: [ { path: 'menu1', component: () => import('@/views/test/menu1/index'), // Parent router-view name: 'Menu1', redirect: '/test/menu1/menu1-1', children: [ { path: 'menu1-1', component: () => import('@/views/test/menu1/menu1-1'), name: 'Menu1-1' }, { path: 'menu1-2', component: () => import('@/views/test/menu1/menu1-2'), name: 'menU1-2'}]}]} // Data can be used by addRoute using const nestedRouter = {path: '/test', Component: Layout, redirect: '/test/menu1/menu1-1', name: 'test', children: [ { path: 'menu1-1', component: () => import('@/views/test/menu1/menu1-1'), name: 'Menu1-1' }, { path: 'menu1-2', component: () => import('@/views/test/menu1/menu1-2'), name: 'Menu1-2' } ] }Copy the code
  • Method two: start with the source code

The key point is how to match the Cache that you want to delete. From now on, it seems that if you want to implement the matching condition from the source code, you can only temporarily solve the problem. But if you want to apply to all situations, this way is not easy to implement. You can write a keep-alive component yourself if you feel the need.

What to write is not good, welcome every big guy to correct