keep-alive使用以及動態緩存頁面

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)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章