vue-element-admin 中三级路由缓存问题

3,196 阅读3分钟

写法问题,实现三级嵌套,二级路由 需要继承空模板


import Empty from '@/layout/empty.vue'

{
    name: 'One',
    path: '/one',
    component: 'Layout',
    alwaysShow: true,
    meta: { title: '一级', icon: 'tool' },
    children: [
      {
        name: 'Empty',
        path: '/one/two',
        component: Empty,
        alwaysShow: true,
        meta: { title: '二级', icon: 'tool' },
        children: [
          {
            name: 'Three',
            path: '/one/two/three',
            component: () =>import('@/views/three/index'),
            meta: { title: '三级', icon: 'build' }
          }
        ]
      }
    ]
}

empty.vue 如果想要三级页面缓存的话一定要加 keep-alive 不然缓存不上,不需要缓存就不用加

<template>
  <keep-alive :include="cachedViews">
    <router-view :key="key" />
  </keep-alive>
</template>
<script>
export default {
  name: 'Empty',
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews
    },
    key() {
      return this.$route.path
    }
  }
}
</script>

三级路由 不缓存 的问题 原因

keep-alive的组件依赖cachedViews,cachedViews是store中的一个状态, cachedViews的逻辑在src/layout/TagView,当路由变更时就会调用addViewTags,addViewTag会根据匹配的路由name属性进行缓存。而用到三级路由的时候,拿到的name只能是第三级路由的name,二级路由继承的模板的名字会丢失,keep-alive就不会进行缓存。


解决办法

在 tagsView.js 中 缓存中提前加上二级菜单得 name

const state = {
  visitedViews: [],
  cachedViews: ['Empty']
}

上述方法可以 解决缓存问题,但随后出现的问题是,关闭三级页面后 虚拟dom 还是存在,造成不好的后果就是,一是内存占用过大 浪费资源,再就是 当你切换路由和在tab标题签上右键刷新时 会调用 那些没 清除的缓存页面createdmounted

当时博主也很纳闷,明明只剩下了 首页,再打开新页面的时候,NetWork 里有许多之前已经关闭了的 页面的 接口请求

借助vueDevTools 后,真相水落石出

vueDevTools

每次打开三级页面后都会多一个 Emptty dom,想过后才知道,\src\layout\components\AppMain.vueempty.vue 两个模板都加了 keep-alive, 而所有的三级页面的父级模板都是上文中的Empty,这就导致了 只要打开三级页面 就会出现一个 Emptty,并且因为三级路由缓存的需要,并不能准确的知道何时需要清除二级模板的缓存

所以 博主目前 的 解决方案是 将三级路由 全部提升至 二级路由 变成以下格式,对于路径展示 还是三级路由的效果

 {
    name: 'One',
    path: '/one',
    component: 'Layout',
    alwaysShow: true,
    meta: { title: '一级', icon: 'tool' },
    children: [
        {
            name: 'Three',
            path: '/one/two/three',
            component: () =>import('@/views/three/index'),
            meta: { title: '/二级/三级', icon: 'build' }
        }
    ]
}

对于 路由设置是从接口获取数据的 就需要在store 中 permission.js 中请求路由时 自己加工处理下了

   // 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return JSON.parse(JSON.stringify(asyncRouterMap)).filter(route => {
    // 处理 vue-router所需要路由 Empty(继承Empty模板的层)的children全部提到上一层
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
    // 拼装路由
    if (lastRouter && route.path.indexOf('http') === -1) {
      route.path = lastRouter.path + '/' + route.path
    }

    if (route.component) {
      // Layout组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'Empty') {
        route.component = Empty
      } else {
        route.component = loadView(route.component) // route.component是一个字符串 这里是字符串转组件对象
      }
    }
    // 如果有子集 递归调用
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    }
    return true
  })
}
//
function filterChildren(childrenMap, lastRouter = false) {
  var children = []
  JSON.parse(JSON.stringify(childrenMap)).forEach((el, index) => {
    if (el.children && el.children.length) {
      if (el.component === 'Empty') {
        el.children.forEach(c => {
          c.path = el.path + '/' + c.path
          if (c.children && c.children.length) {
            children = children.concat(filterChildren(c.children, c))
            return
          }
          children.push(c)
        })
        childrenMap.splice(index, 1)
        return
      }
    }
    if (lastRouter) {
      el.path = lastRouter.path + '/' + el.path
    }
    children = children.concat(el)
  })
  return children
}
export const loadView = view => {
  // 路由懒加载
  return () => import(`@/views/${view}`) // 异步动态加载
}

注意:原文作者中也有提到,由于目前 keep-aliverouter-view 是强耦合的,而且查看文档和源码不难发现 keep-aliveinclude 默认是优先匹配组件的 name ,所以在编写路由 router 和路由对应的 view component 的时候一定要确保 两者的 name 是完全一致的。(切记 name 命名时候尽量保证唯一性 切记不要和某些组件的命名重复了,不然会递归引用最后内存溢出等问题)

原文-文档

欢迎关注我的公众号:前端技术战(注意,是战斗呦)

如果可以

我的博客

我的掘金

我的简书

Laravel China