vue多級菜單(路由)致使緩存(keep-alive)失效

通常的後臺管理系統功能都比較繁多,存在有多級菜單的需求,可是在這種項目裏每每keep-alive的表現卻很是不穩定,有時候某個頁面能夠緩存,可是點幾下就發現緩存丟了;有時候不知道怎麼回事又死活不緩存了。vue

形成這個問題的緣由是: 多級路由組件嵌套。
具體分析: 假如一個後臺管理系統,有一個main.vue是全部頁面的框架, 裏面有這樣一段代碼vuex

<keep-alive>
            <router-view v-if="$route.meta.keepAlive"></router-view>
          </keep-alive>
          <router-view v-if="!$route.meta.keepAlive"></router-view>

而後創建了一個父級菜單,頁面是ChildView.vue, 裏面的代碼以下緩存

<router-view></router-view>

假設這個父菜單裏有兩個子菜單,分別爲 A和B, 而後A設置爲緩存(keepAlive=true), B不緩存(keepAlive=false);
當點擊A菜單的時候,系統緩存了ChildView.vue組件。
當點擊B菜單的時候,因爲B設置的不緩存,因此致使ChildView.vue組件被銷燬
這就是keep-alive爲何會失效的根本緣由。框架

還有更復雜的,好比同一個菜單裏的子菜單能夠緩存,可是點擊另一個父菜單或者父菜單下的子菜單卻不緩存, 究其原理和上面的分析是同樣的緣由,就是多級菜單,多個共用組件致使的keepAlive緩存失效, keepAlive根本沒考慮到頁面緩存的複雜性。異步

如下幾種表現也是這個問題形成的緣由之一:ide

  1. activated和deactivated不觸發
  2. 從A頁面進入B頁面發現的時候發現A頁面的接口又會被重複觸發調用

分析問題:
既然是多個router-view嵌套而且共用的狀況下形成的,那麼若是隻存在一個router-view,也就是隻須要main.vue做爲框架內全部頁面的容器,就不會有這個問題。code

其實是不是多級菜單對於項目或者業務上來說一點都不影響,只是界面顯示上須要,讓用戶能更快點擊到本身須要的功能頁面而已。 既然這樣的話,顯示的菜單保留多級的,實際的router弄成一級, 將顯示菜單和業務router分離開。orm

解決問題:
首先將配置好的多級router用vuex緩存起來,用做展現的菜單。
而後將router轉換一下,轉換成一級菜單,用addRoutes異步添加到router裏面。router

局部示例代碼:接口

const formatRouter = (routes, newRoutes = []) => {
  routes.map(item => {
    if (item.children && item.children.length > 0) formatRouter(item.children, newRoutes);
    newRoutes.push(item);
  })
  return newRoutes;
}
let flatRoutes = formatRouter(routes);
router.addRoutes(flatRoutes);

而後麪包屑導航要調整一下,大部分邏輯都是從route.matched裏面獲取的,可是如今router全是一級的,咱們要從展現的菜單數據裏面拿麪包屑導航數據。

示例代碼:

/**
 * 自定義查找字段, 根據最後一級某個字段查找完整樹(整個父類)
 * @param {*} val     要查找對比的值
 * @param {*} data    要查找的數據
 * @param {*} fKey    要查找對比的字段
 */ 
const recursiveTreeByLastLevel = (val, data, fKey = 'value') => {
  let rData = [];
  for (let i = 0, len = data.length; i < len; i++) {
    rData.push(data[i]);
    if (data[i].children && data[i].children.length > 0) {
      rData = rData.concat(recursiveTreeByLastLevel(val, data[i].children, fKey));
      if (rData.some(item => item[fKey] === val)) return rData;
    }
    if (data[i][fKey] === val) return rData;
    rData = [];
  }
  return rData;
}
 
router.afterEach((to, from, next) => {
  var routerList = recursiveTreeByLastLevel(to.name, store.state.sidebarMenu, 'name')
  store.commit('setCrumbList', routerList) // 經過vuex緩存
})

搞完,收工!

相關文章
相關標籤/搜索