业务中经常遇到一种需求 , 项目中使用了tab_bar管理打开的页面 , 切换过程中希望能缓存下来页面状态 , 关闭标签页 , 再从左侧菜单栏打开页面会重新加载 .
以下是几种常见的解决方案 , 以及优缺点和局限性
keep-alive的官方文档:
https://cn.vuejs.org/v2/api/#keep-alive
适用 : 原本的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
},
适用 : 所有关联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>
适用 : 系统中存在部分匿名组件,需要灵活地控制keep-alive缓存
<div class="firame-router">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
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
},
关闭当前页签时,会触发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()
},
如果被关闭的页签不是当前路由所在的页签 , 就无法触发 路由守卫 (因为没有路由变化) ,
需要我们在关闭页面的地方做一些操作
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
},
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)
}
}