vuex+ keep-alive搭建一套可缓存tab页的web框架

1,841 阅读3分钟
当你在用vue+element开发web端的多页签应用时,大部分是需要缓存的用keep-alive通过配置router.js的方案很好实现

原理

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

            

代码组织和设计

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

          

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

主要是通过name的对比来确定是否需要缓存,当name不匹配是默认不缓存

入口文件缓存路由

在 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>
<script>   
 export default { 
       watch: {   
         $route() {      // 路由组件名称(自定义)                    
            const componentName = this.$route.matched[0]["components"]["default"]["name"]; 
               const detail = this.$route.path;       // 当前路由匹配到name 
                    const name = this.$route.meta[0]["name"]; 
               this.$store.dispatch("commitToolBar", { name, detail, componentName });
            }      
         }    
} 
</script>

关于store代码

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

如果关闭是当前处于激活的标签页,关闭之后。处于激活的标签页就默认是最后一个打开的标签页。

如果当前标签页是最后一个(处于激活状态),则关闭后自动默认它的前一个为默认激活标签页。

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

生命周期 activated、deactivated 

采用了 keep-alive 缓存的路由组件,重新进入该路由,路由组件不会重新创建,所以也就不会触发组件的生命周期函数(比如说 beforeCreate 、 mounted 等)。所以在对该页面进行数据更新或者清除数据。 vue 为我们提供了 activated 和 deactivated 生命周期函数,当重新进入路由组件会触发 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>

入口文件缓存路由App.vue

在 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>
 <script>
  export default {
    watch: {
      $route() {
      // 路由组件名称(自定义)
       const componentName =this.$route.matched[0]["components"]["default"][ "name"];
       const detail = this.$route.path;
       // 当前路由匹配到name
       const name = this.$route.meta[0]["name"];
       this.$store.dispatch("commitToolBar", { name, detail, componentName });
      }
    }
  }
 </script>

总结

以上所述就是vuex + keep-alive实现tab标签页面缓存问题,希望对大家有所帮助,如果大家有任何疑问请抛出问题。

想了解移动端应用的vuex+keep-alive缓存

补充一下

如果你不想让它缓存,比如你打开一个新建页面-填写信息-点击确认后直接回到上个页面,而不是回到上个页面,新建页面还存在。对于这个问题有两种方法可以解决这个问题,一般用第一种:

方法一

// 方法一        
this.$store.dispatch('delVisitedViews', this.$route);
        this.$router.go(-1);
        or
this.$store.dispatch('delVisitedViews',
 { name:this.$route.name,
  title:this.$route.meta.name, 
  path:this.$route.path}).then(()=>{
            this.$router.push({path:''},
(route)=>{ this.route.isReflesh=true  })
})

方法二:

// 方法二
this.$store.state.tagsView.visitedViews.splice(this.$store.state.tagsView.visitedViews.findIndex(item => 
item.path === this.$route.path), 1) 
this.$router.push(this.$store.state.tagsView.visitedViews[this.$store.state.tagsView.visitedViews.length - 1].path)

另外给一下关于嵌套路由的实例;

router.js里的写法:

{            
path: '', 
component: Layout,
name: 'keyEvent',
alwaysShow: true,
meta: { title: 'keyEvent', icon: 'navmenu-business-setting' },
children: [
{  
path: 'eventSearch',
component: () => 
 import( '@/views/energy/keyEvents/eventSearch'),
name: 'eventSearch', 
meta: {
title: 'eventSearch', 
authCode: ['eap.keyEvent.eventSearch']
}
},]        
},

eventSearch.vue里的写法:

 <script>
  export default {
    name:'eventSearch'
    ....
  }
 </script>

这两个name是关键所在,当router.js里的name与eventSearch.vue里的name不匹配就不会缓存。