当前位置: 代码迷 >> 综合 >> keepAlive 单页应用多标签 路由缓存的设计
  详细解决方案

keepAlive 单页应用多标签 路由缓存的设计

热度:61   发布时间:2023-12-16 16:23:18.0

由于使用了多标签显示方式,缓存的设计需求分析

1、设计需求

  • 可以配置一部分路由需要缓存,一部分不需要缓存

  • 在已打开的标签页栏中,切换标签页,使用缓存,不要重新渲染组件

  • 关闭已打开的标签页时,同时清除对应标签页组件缓存

  • 通过点击导航菜单或者页面按钮,打开的路由标签不要使用缓存,需要重新渲染或刷新已打开的标签页

2、代码实现

2.1、store/index.js 定义全局变量 keepAliveList 和 操作方法

增加一个全局变量 keepAliveList ,用来存储需要缓存的组件name,默认值为 ['home']

keepAliveList:['home'],

增加对 变量 keepAliveList 的值更新方法

// 添加缓存路由组件ADD_KEEP_ALIVE(state, route){if (state.keepAliveList.includes(route.name)) {return}state.keepAliveList.push(route.name)console.log('add 缓存' + route.name)},// 移除缓存路由组件DEL_KEEP_ALIVE(state, route){const index = state.keepAliveList.indexOf(route.name)if(index > -1){state.keepAliveList.splice(index, 1)console.log('del 缓存' + route.name)}},// 清除缓存路由组件CLEAR_KEEP_ALIVE(state){state.keepAliveList = ['home']},

2.2、App.vue 中操作更新 keepAliveList

在添加标签或点击导航、页面按钮触发路由跳转时,watch $route 里执行方法 addTab , 此时需要添加缓存,如果已存在要跳转的路由缓存,则要刷新缓存

<template><div><div v-if="showApp == true">
?<!-- 头部 --><sy-Head @on-refrash="reload"></sy-Head>
?<!-- 页标签显示栏 --><sy-Click :titles="history_list" @removeAll="removeAll" @removeTag="removeTab" @changeTag="changeTag"></sy-Click>
?<!-- 导航 --><sy-Nav @showPage="showPage"></sy-Nav>
?<!-- 页面主体内容 --><keep-alive :include="cachedViews"><router-view v-if="$route.meta.keepAlive" :key="key"></router-view></keep-alive><router-view v-if="!$route.meta.keepAlive"></router-view>
?</div>
?......</div>
</template>
?
<script>import syHead from './pages/syHead.vue'import syNav from './pages/syNav.vue'import home from './pages/home.vue'import syClick from './pages/syClick.vue'var _self;
?export default {name: 'app',components: {home,syNav,syHead,syClick},data() {return {
?// 用来控制应用加载时页面延迟显示showApp: false,
?routers: [],history_list: [{"title": "系统首页","path": "/home","curr": true,"name": "home","flag": "",_route: 'home', // 用来存储对应的路由只读实例对象"route": {"name": "home","meta": {"title": "系统首页","requireAuth": false,"showHeader": true,"showNav": true,"showtag": true,"showPosition": true,"showClick": true},"path": "/home","query": {},"params": {},"fullPath": "/home"}}], // 历史标签
?// 标签点击标识,用来控制切换路由时的缓存处理方式,如果是标签点击切换的路由,直接用缓存不需要处理,并且也不需要执行新增标签方法,// 如果不是标签点击的,那么要判断当前缓存中是否已存在将要跳转的路由,如果存在要先清除缓存isTabClick: false,// 键盘control是否被按下isUpKey_Control: false,}},created() {_self = this;},mounted: function() {// 得到浏览器内框高度window.onresize = function() {setTimeout(() => {_self.$store.commit('getCss');}, 300)}window.onresize();
?/**************** 下面两个键盘监控是用来控制F5刷新组件,Control+F5刷新浏览器  ************/// 监控键盘按下事件document.onkeydown = function(e) {let keyNum = window.event ? e.keyCode : e.which; //获取被按下的键值 // 键盘按下control键时if (keyNum == 17) {_self.isUpKey_Control = true;}// 键盘按下F5键时if (keyNum == 116) {if (_self.isUpKey_Control == false) {// 刷新当前路由组件_self.reload()// 阻止浏览器刷新return false}}}// 监控键盘按键抬起事件document.onkeyup = function(e) {let keycode = window.event ? e.keyCode : e.which;// 键盘按下control键抬起后if (keycode == 17) {_self.isUpKey_Control = false;}}
?// 延迟1秒显示setTimeout(() => {this.showApp = true}, 1000);},
?methods: {
?// 重置缓存:先删除在添加resetKeepAive(route) {this.deleteKeepAive(route);this.$nextTick(() => {//添加缓存路由this.addKeepAive(route);console.log('重置缓存')})},
?// 删除缓存deleteKeepAive(route) {this.$store.commit('DEL_KEEP_ALIVE', route);},
?// 添加缓存addKeepAive(route) {if(route.meta.keepAlive){this.$store.commit('ADD_KEEP_ALIVE', route);}},
?// 导航菜单点击showPage: function(router) {this.isTabClick = false;this.$router.push(router);},
?// 切换标签changeTag(index) {this.isTabClick = true;for (let i = 0; i < this.history_list.length; i++) {this.history_list[i].curr = false;}this.history_list[index].curr = true;this.$router.push(this.history_list[index]._route);},?// 不是标签点击的 (isTabClick == false)addTab(to) {// 判断是否要显示tab标签if (!to.meta || !to.meta.showtag) {return}let haved = false;
?// 查找要转向的路由是否在已打开的标签列表里for (let i = 0; i < this.history_list.length; i++) {this.history_list[i].curr = false;
?// 如果当前转向的路由,已经在标签列表中,则直接设置对应的标签焦点状态 (如果路由有参数的,需要判断关键参数flag)if (this.history_list[i].title == to.meta.title &&(!to.query || !to.query.flag || this.history_list[i].flag == to.query.flag)) {haved = true;this.history_list[i].curr = true;
?// 如果当前点击的路由已经在缓存列表中,则先清除缓存列表,再添加;this.resetKeepAive(to)break;}}
?if (!haved) {this.history_list.push({title: to.meta.title,path: to.path,curr: true,name: to.name,flag: (to.query && to.query.flag) ? to.query.flag : "",_route: to,route: {"name": to.name,"meta": to.meta || {},"path": to.name || '',"query": to.query || {},"params": to.params || {},"fullPath": to.fullPath || ""}})this.addKeepAive(to)}},
?// 关闭标签-清除标签页组件缓存(清除缓存时是根据组件name来清除的,所以组件name相同的多个标签页缓存会同时被清除)removeTab(index) {this.isTabClick = true;let name = this.history_list[index].name;// 清除缓存this.deleteKeepAive(this.history_list[index]);// 如果关闭的是当前活动标签页if (this.history_list[index].curr) {this.history_list.splice(index, 1)if (this.history_list.length == 1) {this.$router.push('/home');} else {this.$router.push(this.history_list[index - 1]._route);}} else {this.history_list.splice(index, 1);}},
?// 关闭所有标签removeAll() {for (let i = this.history_list.length - 1; i > 0; i--) {this.history_list.splice(i, 1);}this.$store.commit('CLEAR_KEEP_ALIVE');this.$router.push('/home');},
?// 刷新当前标签页组件reload() {
?let _time = new Date().getTime()let index = this.curr_index;// 先清除当前的路由缓存this.deleteKeepAive(this.history_list[index]);
?// 路由查询参数中将_VERSION时间戳参数值刷新let query = this.history_list[index].route.query || {};if (query._VERSION) {this.history_list[index].route.query._VERSION = _time;} else {this.$set(this.history_list[index].route.query, '_VERSION', _time);}// 将参数数组转成地址参数字符串let queryString = '';this.$XEUtils.each(query, function(value, key) {queryString += key + '=' + value + '&'})queryString = queryString.substring(0, queryString.length - 1);// 因为let fullPath = this.history_list[index].route.fullPath || '';this.history_list[index].route.fullPath = this.history_list[index].path + "?" + queryStringthis.$router.push(this.history_list[index].path + "?" + queryString)}
?},computed: {// 当前焦点状态的标签页序号curr_index() {let index = -1;for (let i = 0; i < this.history_list.length; i++) {if (this.history_list[i].curr == true) {index = i;break;}}return index >= 0 ? index : 0;},cachedViews() {return this.$store.state.keepAliveList},key() {return this.history_list[this.curr_index].route.fullPath},},watch: {// 监听路由变化,自动新增标签'$route': {handler(to, from) {
?// 如果不是标签头点击跳转的路由,要新增标签头if (this.isTabClick == false) {this.addTab(to);}if (!to.query._VERSION) {to.query._VERSION = new Date().getTime()}this.isTabClick = false//this.posMenu(to.path);},immediate: true}}}
</script>
?

3.3、配置路由器 routes 的自定义属性

这里以 router/g_om/index.js 为例,如果路由需要被缓存,则配置属性 keepAlive = true

const routes = [{path:"/g_om_order_list",component: g_om_order_list,name: "g_om_order_list",meta: {title:'订单管理',requireAuth: false,//需要登录显示showHeader: true ,//头部显示showNav:true,//菜单显示showtag:true,//标签显示keepAlive: true, // 需要缓存},},{path:"/g_om_order_detail",component: g_om_order_detail,name: "g_om_order_detail",meta: {title:'订单详情',requireAuth: false,//需要登录显示showHeader: true ,//头部显示showNav:false,//菜单显示showtag:true,//标签显示keepAlive: true, // 需要缓存},},
]
?
?
export default routes;