如何使用keep-alive动态缓存页面
使用场景:
产品希望在用户在多个页面来回切换的时候,不要丢失查询的结果和填写的表单。以下提供两种解决方案。
常规方案:使用vuex
配合exclude
和include
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews"> // 通过include 和 exclude 决定那些组件进行缓存,注意这里说的
// 是组件。并且cachedView数组存放的是组件的名字,不是$route.name,如果有同名组件这个方法不适用。
<router-view :key="$route.fullPath" />
</keep-alive>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
cachedViews() {
return this.$store.state.tagsView.cachedViews
},
visitedViews() {
return this.$store.state.tagsView.visitedViews
},
key() {
return this.$route.path
}
},
mounted() {}
}
</script>
复制代码
从上面的例子其实不难看出,之后需要做的就是对vuex进行操作了。 至少需要两个方法,add 和delete
const state = {
cachedViews: []
}
const mutation = {
ADD_VIEWS: (state, view) => {
if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
},
DEL_CACHED_VIEW: (state, view) => {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
}
}
const action = {
// 根据需求自己补全
addView: ({dispatch}, view) {
dispatch('addCachedView', view)
...
},
addCachedView: ({commit}, view) {
commit('ADD_VIEWS', view)
},
deleteCachedView:({commit}, view) {
...
},
...
}
export default {
namespaced: true,
state,
mutations,
actions
}
复制代码
非常规方案:存在严重的组件复用和重名
// 对应上文的第一个片段
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive> // 缓存全部页面
<router-view :key="$route.fullPath" />
</keep-alive>
</transition>
</section>
</template>
....
复制代码
下面我尽可能简化代码
- 因为存在组件复用和不同文件夹组件重名的情况,所以
keep-alive
必须存储所有的页面。 - 存储所有的页面如何实现动态缓存路由?
我发现了
$vnode
一个字段叫cache
我在控制台打印this.$route
这个cached正是我们需要的控制缓存的数组,之后的操作就简单了。
根据closeSelectedTag
传入的view
获取事件的target
,循环,正则匹配,最后delete
就可以了。
下面是我的处理
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<router-link
v-for="tag in cachedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
class="tags-view-item"
@click.middle.native="closeSelectedTag(tag)"
>
{{ tag.title }}
<span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)"></span>
</router-link>
</scroll-pane>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
export default {
data() {
return {
}
},
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
},
cache: {
get() {
if (this.$route.matched[1]) {
const instances = this.$route.matched[1].instances
console.log('instance', instances.default.$vnode.parent.componentInstance)
return instances.default.$vnode.parent.componentInstance.cache
} else {
return
}
},
set(val) {
console.log('set')
this.$route.matched[1].instances.default.$vnode.parent.componentInstance.cache = val
}
}
},
watch: {
$route() {
this.addTags()
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
mounted() {
this.addTags()
},
methods: {
...
closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
console.log('delete')
if (this.isActive(view)) {
this.toLastView(visitedViews, view)
}
})
const cache = this.cache
const str = RegExp('.*' + view.fullPath)
let key = ''
Object.keys(cache).forEach(el => {
if (str.test(el)) {
key = el
}
})
delete cache[key]
}
...
}
</script>
复制代码