由于使用了多标签显示方式,缓存的设计需求分析
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 + "?" + queryString
this.$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;