keep-alive 的使用
業務中經常遇到一種需求 , 項目中使用了tab_bar管理打開的頁面 , 切換過程中希望能緩存下來頁面狀態 , 關閉標籤頁 , 再從左側菜單欄打開頁面會重新加載 .
以下是幾種常見的解決方案 , 以及優缺點和侷限性
keep-alive的官方文檔:
https://cn.vuejs.org/v2/api/#keep-alive
1: 使用router-view 配合 v-if :
適用 : 原本的router關聯的組件存在匿名組件(沒有設置name屬性)
優點 : 改造成本低
缺點 : 一旦有新打開的頁面 , 會重新加載keep-alive組件 , 丟失所有緩存
// home.vue
// 通過pageLoding強制清空keep-alive緩存
<div class="firame-router" v-if="!$store.state.pageLoding">
<keep-alive>
<router-view v-if="$store.state.bar_data_name.indexOf($route.name)>-1"></router-view>
</keep-alive>
<router-view v-if="$store.state.bar_data_name.indexOf($route.name)<0"></router-view>
</div>
// store/index.js
// 重置bar_data的同時初始化一個保存menu_name的數組
refresh_bar_data(s, v) {
s.bar_data = v
// 初始化bar_data_name
let bar_data = v
let bar_data_name = []
bar_data.forEach(element => {
let name = s.menu_map.get(element).code
bar_data_name.push(name)
});
s.bar_data_name = bar_data_name
},
2: 使用 keep-alive 的 include 參數
適用 : 所有關聯router的頁面組件都設置了name屬性
缺點 : 原項目存在匿名組件 , 需要補充添加 name 屬性 (include只識別組件name , 而非router_name)
// include中的參數需要與被緩存的組件name對應(不是router_name)
<div class="firame">
{{$store.state.bar_data_name}} // 打印當前bar信息
<div class="firame-router">
<keep-alive>
<router-view :include="$store.state.bar_data_name"></router-view>
</keep-alive>
</div>
</div>
3: keep-alive動態清除緩存 (推薦)
適用 : 系統中存在部分匿名組件,需要靈活地控制keep-alive緩存
3.1 首先對所有組件進行緩存
<div class="firame-router">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
3.2 vuex中保存一份當前打開的 tab_bar 數據(具體寫法因人而異 , 此處只展示思路)
refresh_bar_data(s, v) {
s.bar_data = v
// 初始化bar_data_name
let bar_data = v
let bar_data_name = []
bar_data.forEach(element => {
let name = s.menu_map.get(element).code
bar_data_name.push(name)
});
s.bar_data_name = bar_data_name
},
3.3 在mixin文件中定義路由守衛(關鍵)
關閉當前頁籤時,會觸發beforeRouteLeave , 判斷當前關閉的tab是否存在於tab_bar列表中,
如果不在,將清除當前組件緩存
beforeRouteLeave(to, from, next) {
let flag = true
this.$store.state.bar_data_name.forEach(e => {
// bar_data_name存儲打開的tabs的組件路由
if(from.name == e) {
flag = false
}
})
if(flag && this.$vnode.parent && this.$vnode.parent.componentInstance.cache) {
let key = this.$vnode.tag.split('-')[2] // 當前關閉的組件名key
let cache = this.$vnode.parent.componentInstance.cache // 緩存的組件
let keys = this.$vnode.parent.componentInstance.keys // 緩存的組件名
if(cache[key] != null) {
delete cache[key]
let index = keys.indexOf(key)
if(index > -1) {
keys.splice(index, 1)
}
}
}
next()
},
3.4 在tab_close 的函數中對其他非當前路由的頁面標籤進行緩存處理
如果被關閉的頁籤不是當前路由所在的頁籤 , 就無法觸發 路由守衛 (因爲沒有路由變化) ,
需要我們在關閉頁面的地方做一些操作
tabs_close(m) {
// 先讓路由跳轉到被關閉頁面,refresh_bar_data會跳轉active頁面(觸發beforeRouteLeave)
if (this.$parent.active_menu_key != m.id) {
this.$router.push({ name: m.code });
}
let set = new Set(this.bar_data)
set.delete(m.id)
let data = Array.from(set)
let key = data[this.index] || data[this.index - 1]
this.$store.commit('refresh_bar_data', [...set])
this.$parent.active_menu_key = key
},
3.5 快捷關閉當前所有標籤的時候
clear_all_tabs() {
this.$store.commit('refresh_bar_data', [])
this.$parent.active_menu_key = -1
// 清空掉所有的keep-alive緩存
// this.$parent || home.vue組件
// this.$parent.$refs['router_view'] || router_view加載的組件
// this.$parent.$refs['router_view'].$vnode.parent.componentInstance.cache || keep-alive緩存
try {
this.$parent.$refs['router_view'].$vnode.parent.componentInstance.cache = []
} catch (error) {
console.log(error)
}
}