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
否則緩存不上,不須要緩存就不用加html
<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就不會進行緩存。前端
解決辦法vue
在 tagsView.js 中 緩存中提早加上二級菜單得 namegit
const state = {
visitedViews: [],
cachedViews: ['Empty']
}
複製代碼
上述方法能夠 解決緩存問題,但隨後出現的問題是,關閉三級頁面後 虛擬dom 仍是存在,形成很差的後果就是,一是內存佔用過大 浪費資源,再就是 當你切換路由和在tab標題簽上右鍵刷新時 會調用 那些沒 清除的緩存頁面created
和mounted
github
當時博主也很納悶,明明只剩下了 首頁,再打開新頁面的時候,NetWork 裏有許多以前已經關閉了的 頁面的 接口請求vue-router
藉助vueDevTools 後,真相水落石出api
每次打開三級頁面後都會多一個 Emptty
dom,想事後才知道,\src\layout\components\AppMain.vue和empty.vue 兩個模板都加了 keep-alive
, 而全部的三級頁面的父級模板都是上文中的Empty
,這就致使了 只要打開三級頁面 就會出現一個 Emptty
,而且由於三級路由緩存的須要,並不能準確的知道什麼時候須要清除二級模板的緩存緩存
因此 博主目前 的 解決方案是 將三級路由 所有提高至 二級路由 變成如下格式,對於路徑展現 仍是三級路由的效果markdown
{
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 中請求路由時 本身加工處理下了dom
// 遍歷後臺傳來的路由字符串,轉換爲組件對象
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 命名時候儘可能保證惟一性 切記不要和某些組件的命名重複了,否則會遞歸引用最後內存溢出等問題)
歡迎關注個人公衆號:前端技術戰(注意,是戰鬥
的戰
呦)
若是能夠