一、效果图

实现原理利用 keep-alive :include
当点击菜单,判断keepAliveList是否含有该菜单,如果没有,则把菜单页面name直接添加进keepAliveList,如果有,则删除,在进行添加
在这里插入图片描述

二、主要代码

1、index页面 主要代码

<keep-alive :include="keepAliveList">
  <router-view class="avue-view" v-if="keepAlive"/>
</keep-alive>

provide() {
   return {
     index: this,
     routerRefresh: this.routerRefresh
   };
 },
  data() {
    return {
      keepAlive:true,
      keepAliveList: [],
    };
  }, 
 watch: {
    "$store.state.common.keepAliveList": {
      handler: function (val, oldVal) {
        this.keepAliveList = val;
      },
      deep: true,
      immediate: true,
    },
  },
  routerRefresh() {
      this.keepAlive = false;
      this.$nextTick(() => {
        this.keepAlive = true;
      });
    },

2、sidebarItem 页面 methods

//删除缓存
delKeepAliveList(name) {
   if (this.$store.state.common.keepAliveList.includes(name)) {
      this.$store.commit("delKeepAliveList",name);
   }
 },
 //设置缓存
 setKeepAliveList(name) {
      this.$store.commit("setKeepAliveList",name);
 },
 open(item) {
   this.routerRefresh();
   let path = item.path;
   let name = path.substring(path.lastIndexOf("/") + 1); //页面name
   this.delKeepAliveList(name);

   if (this.screen <= 1) this.$store.commit("SET_COLLAPSE");
   if (this.validIsOpen(item)) {
     window.open(item[this.pathKey]);
   } else {
     this.$nextTick(() => {
       this.setKeepAliveList(name);
       this.$router.$avueRouter.group = item.group;
       this.$router.$avueRouter.meta = item.meta;
       this.$router.push({
         path: this.$router.$avueRouter.getPath(
           {
             name: item[this.labelKey],
             src: item[this.pathKey],
           },
           item.meta
         ),
         query: item.query,
       });
     });
   }
 },

3、store页面

state:{
	keepAliveList:getStore({name: 'keepAliveList'})||[],
},
mutations:{
	setKeepAliveList(state, name) {
	   state.keepAliveList.push(name);
	    setStore({ name: "keepAliveList", content: state.keepAliveList });
	  },
	  delKeepAliveList(state, name) {
	    state.keepAliveList = state.keepAliveList.filter(function(i) {
	      return i != name;
	    });
	    setStore({ name: "keepAliveList", content: state.keepAliveList });
	  },
}
 

三、封装mixins(升级)

升级原因:因为前面方法只针对点击菜单,点击其他按钮弹出页面无效,最开始想通过路由全局钩子完成,但是路由一直值不显示,而后改成了mixins方法,及在需要的地方引入即可
keepAlive.js 文件

export default {
  inject: ["routerRefresh"],
  data(){
      return {
        NokeepAlive:[]//配置不缓存的页面name
      }
  },
  methods: {
    //删除缓存
    delKeepAliveList(name) {
      if (this.isKeepAlive(name)) {
        this.routerRefresh();
        if (this.$store.state.common.keepAliveList.includes(name)) {
          this.$store.commit("delKeepAliveList", name);
        }
      }
    },
    //设置缓存
    setKeepAliveList(name) {
      if (this.isKeepAlive(name)) {
        this.$store.commit("setKeepAliveList", name);
      }
    },
    //是否缓存
    isKeepAlive(name) {
      return this.NokeepAlive.includes(name) ? false : true;
    }
  }
};

使用

import keepAlive from "@/mixins/keepAlive";//1、引入文件:
mixins:[keepAlive], //2、加入mixins
//3、获取页面name
let path = item.path;
let name = path.substring(path.lastIndexOf("/") + 1);
//4、判断删除缓存
this.delKeepAliveList(name);
 //5、设置缓存
this.$nextTick(() => {
 this.setKeepAliveList(name);
 }

四、处理iframe报表缓存

实现思路:动态注册报表组件,利用hasOpen 和 $route.path 控制报表是否显示,如果点击的是菜单,则hasOpen设置为false 然后再设置为true,如果点击的是tag标签,则不进行处理。如果刷新了页面,需要从缓存里面取iframe页面的hasOpen属性,否则,当切换页面标签时。hasOpen为false, 则不会显示iframe页面
在这里插入图片描述

1、判断哪些报表需要缓存

const exceptRoute = ["repconfiguration", "reportlist"];//二级目录
let reportKey = ["work"];//一级目录
 //判断是否是有Iframe的报表页面
isHasReportKey(path) {
      let flag = false;
      reportKey.forEach((item) => {
        if (path.indexOf(item) != -1) {
         flag = true;
        }
      });
      return flag;
    },
 isReportIframe(path) {//判断是否是有Iframe的报表页面
   return this.isHasReportKey(path) &&
     !exceptRoute.includes(path.substring(path.lastIndexOf("/") + 1))
     ? true
     : false;
 }

2、通过component组件处理报表组件

<keep-alive :include="keepAliveList">
   <router-view class="avue-view" v-if="keepAlive && !isIframeComp" />
  </keep-alive>
  <component
    class="avue-view rr iframe"
    :is="item.name"
    v-for="item in hasOpenComponentsArr"
    :key="item.index"
    :ref="item.name"
    v-show="$route.path === item.path"
  ></component>

3、监听路由变化,判断是否是含有iframe的页面

//判断当前路由是否是报表
    $route(val) {
      if (this.isReportIframe(val.path)) {
        this.isIframeComp = true;
      } else {
        this.isIframeComp = false;
      }
    },

4、监听菜单变化,注册报表组件

 "$store.state.user.menu": {
      handler: function (n) {
        const componentsArr = this.getComponentsArr(n);
        componentsArr.forEach((item) => {
          Vue.component(item.name, item.comp);
        });
        this.myComponent = componentsArr;
      },
    },

5、动态组将iframe组件

//动态注册iframe组件
    getComponentsArr(routes) {
     let componentsArr = [];
     
      if (this.$store.state.common.iframePage.length > 0) {
          componentsArr = this.$store.state.common.iframePage;
        } 
      let iframeArr = [];
      routes.forEach((element) => {
        let reportPath = element.path.split("/");
        if (reportKey.includes(reportPath[1])) {
          let iframeObj = {};
          element.children.forEach((element1, index) => {
            let name = element1.path.substring(
              element1.path.lastIndexOf("/") + 1
            );
            if (!exceptRoute.includes(name)) {
              let myc = (resolve) =>
                require([`../../views${element1.path}.vue`], resolve);
              iframeObj = {
                name: name,
                path: element1.path,
                hasOpen: false,
                comp: myc,
                index: index,
              };
              iframeArr.push(iframeObj);
            }
          });
        }
      });
       //从缓存里面取值,重新赋值hasOpen
      componentsArr.forEach((store_item)=>{
          iframeArr.forEach((item)=>{
              if(item.name == store_item.name){
                  item.hasOpen = store_item.hasOpen
              }
          })
      })
      return iframeArr;
    },

6、记录已经打开的报表组件

computed:
hasOpenComponentsArr() {
      return this.myComponent.filter((item) => item.hasOpen);
    },

7、处理报表是否显示

isOpenIframePage(clickType) {
  const target = this.myComponent.find((item) => {
     return item.path === clickType.split("-")[1];
   });
   if (this.validatenull(target)) return;
   //如果点击的是tag,则设置为显示状态
   if (clickType.indexOf("menu") != "-1") {
     target.hasOpen = false;
     this.$nextTick(() => {
       target.hasOpen = true;
       this.$store.commit("getIframePage", this.myComponent);
     });
   }
 },

8、处理存储数据(store.state)

//state
iframePage:getStore({name:"iframePage"})||[],
clickIframeMenuType:"menu"

//mutations
getClickIframeMenuType:(state,clickType) => {
  state.clickIframeMenuType = clickType;
},
getIframePage:(state,iframePage)=>{
    state.iframePage = iframePage;
    setStore({ name: "iframePage", content: state.iframePage });
},

9、处理点击事件

//tag点击事件
if(this.isReportIframe(path)){
    this.$store.commit("getClickIframeMenuType",`menu-${item.path}`);
 }
//菜单点击事件
if(this.isReportIframe(item.name) ) {
     this.$store.commit(
       "getClickIframeMenuType",
       `tag-${item.name}`
     );
  }

五、处理三级路由无法缓存的问题

解决思路:扁平化代码

在这里插入图片描述

在这里插入图片描述

参考连接:vue多级路由缓存(keep-alive)失效的解决方案

Logo

前往低代码交流专区

更多推荐