前言:
我的需求是后台管理系统,
左侧有menu(切换),上面有tabs(标签,可以切换、关掉、关闭左侧、关闭右侧、关闭其他页面等功能),显示在一块地方(jeecg用的他的模板)
三个方法(两种类型):
1、使用方法,让组件摧毁
this.$destroy()
2、include、extends
<keep-alive :include="keepAliveList">
<router-view />
</keep-alive>
3、想办法用this.$destroy()更方便的写
说明: 模板开始我用的是
<keep-alive>
<router-view v-if="keepAlive" />
</keep-alive>
<router-view v-if="!keepAlive" />
根据路由中的keepAlive来看是否需要缓存,本着能不动尽量不动的原则,我采取了第一种方式强制摧毁组件,但暴露出一个问题,就是无论怎么样也缓存不上了,keepAlive也为true,按理说destroy方法应该是摧毁树的节点,但是无论如何也不行,所以采用了第二种include方法:(jeecg有两个地方有缓存的地方this.$destroy应该也可以,先试试)
先说第二种:
jeecg里面有RouteView.vue文件:
// <keep-alive>
// <router-view v-if="keepAlive" />
// </keep-alive>
// <router-view v-if="!keepAlive" /> -->
//改为
<keep-alive :include="keepAliveList" >
<router-view />
</keep-alive>
//引入
import { mapState } from 'vuex'
//使用
computed: {
...mapState({
keepAliveList: state => state.utils.keepAliveList,
})
}
TabLayout.vue文件:
<keep-alive :include="keepAliveList">
<router-view />
</keep-alive>
在methods里:
//mounted里面执行以下,初始数据给到
stateKeepList(value){
if ( this.linkList.length >1){
if(value.meta.keepAlive){
let changKey1 = value.fullPath.split('/')[value.fullPath.split('/').length -1]
this.$store.dispatch('TOSETPUSH_CHANGEKEEPALIVELIST',changKey1)
}
}
let changKey2 = this.linkList[0].split('/')[this.linkList[0].split('/').length -1]
this.$store.dispatch('TOSETPUSH_CHANGEKEEPALIVELIST',changKey2)
},
在watch里面有个监听路由的:
watch: {
$route: function (newRoute) {
//else if (this.linkList.indexOf(newRoute.fullPath) < 0) 这里面
if(newRoute.meta.keepAlive){
let jcNumber = newRoute.name.split('-')[newRoute.name.split('-').length -1]
this.$store.dispatch('TOSETPUSH_CHANGEKEEPALIVELIST',jcNumber)
}
}
}
有关闭左边、关闭右边、关闭其他方法:
//依赖this.linkList所以都要放在方法末尾
unifiedHandling(){
let delLeftKeepAlive = []
for(let i =0;i<this.linkList.length;i++){
let arrOne = this.linkList[i].split('/')[this.linkList[i].split('/').length -1]
delLeftKeepAlive.push(arrOne)
}
this.$store.dispatch('TOSET_CHANGEKEEPALIVELIST',delLeftKeepAlive)
},
vuex里面:
const uitls = {
state: {
screenWidth: 200,
menuImgCur: true,
keepAliveList: [],
},
mutations: {
SET_SCREENWIDTH:(state, fixed)=>{
state.screenWidth = fixed
},
SET_REDACTMENUIMGCUR:(state,fixed)=>{
state.menuImgCur = fixed
},
// 路由信息List方法
SETPUSH_CHANGEKEEPALIVELIST:(state,fixed)=>{
state.keepAliveList.push(fixed)
},
SET_CHANGEKEEPALIVELIST:(state,fixed)=>{
state.keepAliveList = [...new Set(fixed)]
console.log(state.keepAliveList,'组件地址更换')
}
},
actions: {
// 宽度
TOSET_SCREENWIDTH({commit},value){
commit('SET_SCREENWIDTH',value)
},
// menu 的菜单模式
TOSET_REDACTMENUIMGCUR({commit},value){
commit('SET_REDACTMENUIMGCUR',value)
},
// 路由信息List方法
TOSETPUSH_CHANGEKEEPALIVELIST({commit},value){
commit('SETPUSH_CHANGEKEEPALIVELIST',value)
},
TOSET_CHANGEKEEPALIVELIST({commit},value){
commit('SET_CHANGEKEEPALIVELIST',value)
},
}
}
export default uitls
下面就不是很推荐了,也存在很多问题没有去改,还是那个问题,destroy无法再次缓存问题,后面再写
核心:清除cache对象里面的信息,和组件自调用this.$destroy();
一、 路由方法,可以小改
在main.js文件中
Vue.mixin({
beforeRouteLeave:function(to, from, next){
console.log('111111111111111触发了')
console.log(this)
// 确定是点击关闭按钮过来的 关闭的路由时可缓存的
if(from.meta.reload&&from.meta.keepAlive&&this.$vnode.componentOptions) {
from.meta.reload = false
var key = this.$vnode.key == null
? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '')
: this.$vnode.key;
var cache = this.$vnode.parent.componentInstance.cache;
var keys = this.$vnode.parent.componentInstance.keys;
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key);
if (index > -1) {
keys.splice(index, 1);
}
}
delete cache[key];
}
this.$destroy();
}
next();
},
});
//remove方法中加入这一段
remove(){
// this.pageList.map( item => {
// if(item.fullPath === key ){
// console.log(item)
// this.$router.push(item.path)
// // this.go()
// item.meta.reload = true
// // this.$route.meta.reload=true
// }
// } )
}
注意的是需要你在tabs关闭的时候需要打上一个
from.meta.reload = true
方法限制很多,不能在在其他页面关闭,因为要执行这个守卫,并且用这个组件的this.$destryu才能摧毁自己
二、修改成一个方法,脱离路由守卫拦截的实现
remove(key) {
console.log(key)
if (key == indexKey) {
this.$message.warning('首页不能关闭!')
return
}
if (this.pageList.length === 1) {
this.$message.warning('这是最后一页,不能再关闭了啦')
return
}
// 找到移除的对象,变成不缓存
console.log(this.thisParent)
console.log(this.everyThat)
let reKet = key
this.pageList.map((item) => {
console.log(item.fullPath, reKet)
if (item.fullPath === reKet) {
console.log(item)
let arr = item.fullPath.split('/')
this.urlName = arr[arr.length - 1]
for (let i in this.thisParent) {
console.log(i)
console.log(this.thisParent[i])
let arr = this.thisParent[i].tag.split('-')
this.comName = arr[arr.length - 1]
console.log(this.comName, this.urlName, false)
if (this.urlName === this.comName) {
this.onselectStr = i
console.log(i)
// 归零
// this.urlName = 1
// this.comName = 2
console.log(this.urlName, this.comName, true)
// 进行删除操作
// var key = this.onselectStr.key == null
// ? this.onselectStr.componentOptions.Ctor.cid + (this.onselectStr.componentOptions.tag ? `::${this.onselectStr.componentOptions.tag}` : '')
// : this.onselectStr.key;
console.log(this.everyThat)
var key = i
console.log(key, 'key')
var cache = this.everyThat.$vnode.parent.componentInstance.cache
console.log(cache, 'cache')
var keys = this.everyThat.$vnode.parent.componentInstance.keys
console.log(keys)
if (cache[key]) {
if (keys.length) {
var index = keys.indexOf(key)
if (index > -1) {
keys.splice(index, 1)
}
}
delete cache[key]
console.log(keys)
}
// 循环thatList this的列表,找到那个组件
// for(let j in this.thatList){
// if(j === this.urlName){
// j()
// }
// }
for (let j = 0; j < this.thatList.length; j++) {
console.log(this.thatList[j])
let arr = this.thatList[j].$vnode.tag.split('-')
console.log(arr)
let str = arr[arr.length - 2]
console.log(str)
console.log(this.urlName)
if (key === str) {
console.log('okjinlaile')
this.thatList[j].$destroy()
return
}
}
// this.everyThat.$destroy();
console.log('zhixingle $destroy')
}
}
}
})
console.log('this.pageList ', this.pageList)
this.pageList = this.pageList.filter((item) => item.fullPath !== key)
let index = this.linkList.indexOf(key)
this.linkList = this.linkList.filter((item) => item !== key)
index = index >= this.linkList.length ? this.linkList.length - 1 : index
this.activePage = this.linkList[index]
console.log('wei')
},
每个组件中:
this.$store.dispatch('TOSET_CHANGETHISPARENT',{cache: this.$vnode.parent.componentInstance.cache,that: this})
vuex中:
state:{
thisParent: {},
everyThat: '',
thatList: [],
},
mutations: {
SET_CHANGETHISPARENT:(state,{cache,that})=>{
state.thisParent = cache
state.everyThat = that
state.thatList.push(that)
// let arr = that.$vnode.tag.split('-')
// let remobj = arr[arr.length -1]
// state.thatList[`${remobj}`] = that.$destroy()
console.log(state.thatList)
}
},
actions: {
// 清缓存
TOSET_CHANGETHISPARENT({commit},{cache,that}){
console.log(cache,that)
commit('SET_CHANGETHISPARENT',{cache,that})
},
}
限制也很大:
1、vuex收集太多组件信息(vuex过大)
2、每个组件都需要在mounted里传this
3、组件的name和文件名要一致,否则根据路由信息和name页面匹配,没结果
三、优化思路
TabLayout页面
clKeepAliveList: [],
watch: {
$route: function (newRoute) {
console.log("新的路由",newRoute)
//处理缓存list
let jcNumber = newRoute.name.split('-')[newRoute.name.split('-').length -1]
console.log(jcNumber)
if(this.clKeepAliveList.indexOf(jcNumber)<0){
this.clKeepAliveList.push(jcNumber)
this.clKeepAliveList =[...new Set(this.clKeepAliveList)]
this.$store.dispatch('TOSETPUSH_CHANGEKEEPALIVELIST',jcNumber)
console.log(this.clKeepAliveList)
}
}}
mounted(){
this.fn()
},
methods:{
fn(){
for(let i= 0;i<this.linkList.length;i++){
let changKey = this.linkList[i].split('/')[this.linkList[i].split('/').length -1]
this.$store.dispatch('TOSETPUSH_CHANGEKEEPALIVELIST',changKey)
this.clKeepAliveList.push(changKey)
}
console.log(this.clKeepAliveList)
},
}
在store的utils:
keepAliveList: [],
// 路由信息List方法
mutations: {
SETPUSH_CHANGEKEEPALIVELIST:(state,fixed)=>{
state.keepAliveList.push(fixed)
},
SET_CHANGEKEEPALIVELIST:(state,fixed)=>{
state.keepAliveList = [...new Set(fixed)]
console.log(state.keepAliveList,'组件地址更换')
}
}
actions: {
// 路由信息List方法
TOSETPUSH_CHANGEKEEPALIVELIST({commit},value){
commit('SETPUSH_CHANGEKEEPALIVELIST',value)
},
TOSET_CHANGEKEEPALIVELIST({commit},value){
commit('SET_CHANGEKEEPALIVELIST',value)
},
}
处理的页面:
// 路由缓存管理
import { mapState } from 'vuex'
export default {
computed: {
...mapState({
keepAliveList: state => state.utils.keepAliveList,
}),
},
watch:{
keepAliveList(e){
console.log(e)
// 监听缓存列表的变化,如果缓存列表中没有当前的路由或组件则在缓存中销毁该实例
let name = this.$options.name;
console.log(name,'执行到这里了')
if (e.indexOf(name) < 0) {
console.log(name,'删除这个组件')
this.$destroy()
}
}
},
}
其他各个页面:
import DelectComm from './smallUtils/minxin/delectComm.js'
mixins: [DelectComm],
现在存在的问题:
1、组件的name和文件名要一致,否则根据路由信息和name页面匹配,没结果
2、因为用的meta的keepalive来做的,所以关闭后出现了一个不能再次缓存的问题