写法问题,实现三级嵌套,二级路由 需要继承空模板
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标题签上右键刷新时 会调用 那些没 清除的缓存页面created
和mounted
当时博主也很纳闷,明明只剩下了 首页,再打开新页面的时候,NetWork 里有许多之前已经关闭了的 页面的 接口请求
借助vueDevTools 后,真相水落石出
每次打开三级页面后都会多一个 Emptty
dom,想过后才知道,\src\layout\components\AppMain.vue和empty.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-alive
和 router-view
是强耦合的,而且查看文档和源码不难发现 keep-alive
的 include 默认是优先匹配组件的 name
,所以在编写路由 router
和路由对应的 view component 的时候一定要确保 两者的 name 是完全一致的。(切记 name 命名时候尽量保证唯一性 切记不要和某些组件的命名重复了,不然会递归引用最后内存溢出等问题)
原文-文档
欢迎关注我的公众号:前端技术战(注意,是战斗
的战
呦)
如果可以
我的博客
我的掘金
我的简书
Laravel China
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!