vuex + keep-alive實現tab標籤頁面緩存

前言

在開發不少管理系統過程之中,常遇到這種需求,須要對打開路由頁面進行緩存,而後在系統頁眉提供方便查閱的tab標籤進行切換以及對已經緩存頁面進行數據刷新和清除數據操做。具體演示以下圖所示:javascript

tab標籤頁

在上面演示中實現了相似window tab標籤頁效果,會對當前數據進行緩存。在瀏覽器中實現對路由頁面的緩存能夠減小接口請求,也方便了用戶來回切換想搜索的數據列表。vue

原理

Vue提供的keep-alive API實現對路由組件的緩存。include屬性能夠綁定一個數組,裏面是須要路由組件的name值,能夠實現對該路由組件進行緩存,若是不須要對路由進行緩存,直接移除該項元素便可。java

api

代碼組織和設計

實現上面的功能,採用vuex進行全局的緩存數據保存,定義爲cacheView;已經打開的路由頁面用toolBarData進行保存。下圖是代碼是代碼設計總體圖:git

須要添加一個路由頁面到cacheView,須要有actions setCacheViewcommit一個change Eventstate數據進行更改,而後修改後的數據會自動派發到App.vue中使用到該數據的位置(即keep-alive處)。而添加標籤頁也是相似的流程,再也不描述。至於爲何要把標籤頁和路由緩存頁面分離成兩個數組,主要是有兩方面的考慮:github

  1. 有些路由頁面須要緩存,而有些路由頁面不須要緩存。可是路由頁面須要添加到標籤頁數組中。
  2. 標籤頁數組須要攜帶更多的參數,單單存儲路由頁面name值不能知足需求。

store代碼實現

store代碼實現以下所示,主要須要比較詳細說明的是clearToolItem,這個函數是清除標籤頁。涉及兩個規則:vuex

  1. 若是關閉是當前處於激活的標籤頁,關閉以後。處於激活的標籤頁就默認是最後一個打開的標籤頁。
  2. 若是當前標籤頁是最後一個(處於激活狀態),則關閉後自動默認它的前一個爲默認激活標籤頁。
import router from '../router'
export default {
    state: {
        toolBarData:[],// 保存標籤button的數組
        cacheView:[] // 保存須要緩存的數組
    },
    getters: {
        getToolData(state){
            return state.toolBarData;
        },
        getCacheView(state){
            return state.cacheView;
        }
    },
    mutations: {
        setToolData(state, data) { // 添加標籤按鈕,若是當前路由已經打開,則再也不重複添加
            const inToolbar = state.toolBarData.find(item => item.detail === data.detail)
            !inToolbar &&  state.toolBarData.push({
                ...data
            });
        },
        setCacheView(state,data){ // 與setToolData相似
            if(state.cacheView.includes(data.componentName)) 
                return;
            state.cacheView.push(data.componentName);
        },
        clearToolItem(state,detail){
            const index = state.toolBarData.findIndex(item => item.detail === detail);
            const isActive = router.app.$route.path == state.toolBarData[index]["detail"];
            const len = state.toolBarData.length - 1;
            state.toolBarData.splice(index,1);
            (index == len || isActive) && router.push({path:state.toolBarData[state.toolBarData.length - 1]["detail"]});
        },
        clearCacheView(state,viewName){
            const index = state.cacheView.findIndex(item => item == viewName);
            state.cacheView.splice(index,1);
        }
    },
    actions: {
        commitToolBar({commit},data) {
            commit("setToolData",data);
            commit("setCacheView",data);
        },
        clearToolBar({commit},data){
            commit("clearToolItem",data.detail);
        },
        clearCache({commit},data){
            commit("clearCacheView",data);
        }
    }
}
複製代碼

入口文件緩存路由

App.vue入口文件,使用keep-alive對匹配的路由組件進行緩存,監聽當前路由變化,添加緩存路由和標籤。api

<template>
    <el-main style="position:relative;margin-top:45px;">
        <!--渲染標籤的地方-->
        <ToolBar></ToolBar>
        <div class="routeWrap">
            <transition name="fade-transform">
                <keep-alive :include="cachedViews">
                    <router-view></router-view>
                </keep-alive>
            </transition>
        </div>
    </el-main>
 </template>
複製代碼

ToolBar代碼

這裏使用了elementuiel-tag標籤,el-tag標籤帶有動畫、關閉按鈕、主題color等屬性,close函數是清除該標籤和清除緩存路由(已訪問過)。click主要是當對該標籤項點擊操做,則切換到該路由頁面。其中active是該標籤匹配到當前路由時候處於激活狀態(顏色高亮),el-tag的動畫比較生硬,因此關閉了。數組

<template>
  <div class="toolbar"> <el-tag class="toolItem" type="info" :disable-transitions="false" :closable="item.id != 0" effect="plain" v-for="(item,index) in getToolData" :key="index" :class="{active:$route.path == item.detail}" @click="redirect(item)" @close="closeToolItem(item)" > <span class="dot" v-if="$route.path == item.detail"></span> {{item.name}} </el-tag> </div> </template>

<script>
import { mapGetters } from "vuex";
export default {
  methods: {
    closeToolItem(item, index) {
      this.$store.dispatch("clearToolBar", item);
      this.$store.dispatch("clearCache", item.componentName);
    },
    redirect(item) {
      this.$router.push({ path: item.detail });
    }
  },
  computed: {
    ...mapGetters(["getToolData", "getCacheView"])
  },
  watch: {
    // 監聽路由變化自動dispatch一個action
    $route() {
        // 路由組件名稱(自定義)
        const componentName =this.$route.matched[0]["components"]["default"][ "name"];
        // 路由組件path
        const detail = this.$route.path;
        // 當前路由須要顯示到tab標籤上名字,如「發佈公告」
        const name = this.$route.meta[0]["name"];
        this.$store.dispatch("commitToolBar", { name, detail, componentName });
    }    
  }
};
</script>
複製代碼

生命週期activateddeactivated

採用了keep-alive緩存的路由組件,從新進入該路由,路由組件不會從新建立,因此也就不會觸發組件的生命週期函數(好比說beforeCreatemounted等)。因此在對該頁面進行數據更新或者清除數據。vue爲咱們提供了activateddeactivated生命週期函數,當從新進入路由組件會觸發activated函數,離開則會觸發deactivated瀏覽器

<template>
    <div> A page</div>
</template>
<script>
    export default {
        data(){
            return {
                form :{
                    name:'',
                    password:''
                }
            }
        },
        activated(){
            this.getList()
        },
        deactivated(){
            Object.keys(this.form).map(key => {
                this.form[key] = ''
            })
        }
    }
</script>
複製代碼

總結

到如今已經完成了相似於window tab標籤,從代碼組織和設計上,能夠比較清除看清代碼組織的原理。這個實現主要是模仿了vue-element-admin這個開源項目。緩存

參考: vue-element-admin

相關文章
相關標籤/搜索