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)
    }
}
Logo

前往低代码交流专区

更多推荐