阅读 647

vuex + keep-alive实现tab标签页面缓存

前言

在开发很多管理系统过程之中,常遇到这种需求,需要对打开路由页面进行缓存,然后在系统页眉提供方便查阅的tab标签进行切换以及对已经缓存页面进行数据刷新和清除数据操作。具体演示如下图所示:

tab标签页

在上面演示中实现了类似window tab标签页效果,会对当前数据进行缓存。在浏览器中实现对路由页面的缓存可以减少接口请求,也方便了用户来回切换想搜索的数据列表。

原理

Vue提供的keep-alive API实现对路由组件的缓存。include属性可以绑定一个数组,里面是需要路由组件的name值,可以实现对该路由组件进行缓存,如果不需要对路由进行缓存,直接移除该项元素即可。

api

代码组织和设计

实现上面的功能,采用vuex进行全局的缓存数据保存,定义为cacheView;已经打开的路由页面用toolBarData进行保存。下图是代码是代码设计整体图:

需要添加一个路由页面到cacheView,需要有actions setCacheViewcommit一个change Eventstate数据进行更改,然后修改后的数据会自动派发到App.vue中使用到该数据的位置(即keep-alive处)。而添加标签页也是类似的流程,不再描述。至于为什么要把标签页和路由缓存页面分离成两个数组,主要是有两方面的考虑:

  1. 有些路由页面需要缓存,而有些路由页面不需要缓存。但是路由页面需要添加到标签页数组中。
  2. 标签页数组需要携带更多的参数,单单存储路由页面name值不能满足需求。

store代码实现

store代码实现如下所示,主要需要比较详细说明的是clearToolItem,这个函数是清除标签页。涉及两个规则:

  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对匹配的路由组件进行缓存,监听当前路由变化,添加缓存路由和标签。

<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

关注下面的标签,发现更多相似文章
评论