vue-element-admin 中三級路由緩存問題

寫法問題,實現三級嵌套,二級路由 須要繼承空模板


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標題簽上右鍵刷新時 會調用 那些沒 清除的緩存頁面createdmountedgithub

當時博主也很納悶,明明只剩下了 首頁,再打開新頁面的時候,NetWork 裏有許多以前已經關閉了的 頁面的 接口請求vue-router

藉助vueDevTools 後,真相水落石出api

vueDevTools

每次打開三級頁面後都會多一個 Emptty dom,想事後才知道,\src\layout\components\AppMain.vueempty.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-aliverouter-view 是強耦合的,並且查看文檔和源碼不難發現 keep-aliveinclude 默認是優先匹配組件的 name ,因此在編寫路由 router 和路由對應的 view component 的時候必定要確保 二者的 name 是徹底一致的。(切記 name 命名時候儘可能保證惟一性 切記不要和某些組件的命名重複了,否則會遞歸引用最後內存溢出等問題)

原文-文檔

歡迎關注個人公衆號:前端技術戰(注意,是戰鬥呦)

若是能夠

個人博客

個人掘金

個人簡書

Laravel China

相關文章
相關標籤/搜索