通常的後臺管理系統功能都比較繁多,存在有多級菜單的需求,可是在這種項目裏每每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
分析問題:
既然是多個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緩存 })
搞完,收工!