效果图
默认情况下没有 noCache属性 或为false 都会进行缓存 true不缓存
再切回来input内容还在
如果为true
再切回来 input内容没有了
右键删除某一个标签导航时也要删除对应路由缓存
效果
关闭后再打开 input内容被清空
5-1 修改store创建缓存列表
再添加标签导航时,同时也判断该路由要不要缓存,要缓存就根据路由配置的name属性进行缓存(路由组件的name要与路由配置的name一致)再添加到keep-alive inludes的缓存列表中。 (keep-alive内部是根据组件的name进行缓存,我们添加到cachedViews缓存列表的name是从每条路由配置的name取得值,所以路由组件和路由配置中必须要有一致的name属性。)
tagsView module
主要就是添加cachedViews缓存集合,再新增用来添加和删除cachedViews缓存列表actions和muations
state
actions (添加、删除)
mutations
src/store/modules/tagsView.ts
import { Module, ActionTree, MutationTree } from 'vuex'
import { RouteRecordRaw, RouteRecordNormalized, RouteRecordName } from 'vue-router'
import { IRootState } from '@/store'
// 携带fullPath
export interface RouteLocationWithFullPath extends RouteRecordNormalized {
fullPath?: string;
}
export interface ITagsViewState {
// 存放当前显示的tags view集合
visitedViews: RouteLocationWithFullPath[];
// 根据路由name缓存集合
cachedViews: RouteRecordName[];
}
// 定义mutations
const mutations: MutationTree<ITagsViewState> = {
// 添加可显示tags view
ADD_VISITED_VIEW(state, view) {
// 过滤去重
if (state.visitedViews.some(v => v.path === view.path)) return
// 没有titles时处理
state.visitedViews.push(Object.assign({}, view, {
title: view.meta.title || 'tag-name'
}))
},
// 如果路由meta.noCache没有 默认或为false代表进行缓存,为true不缓存
// 默认缓存所有路由
ADD_CACHED_VIEW(state, view) {
// 只有路由有name才可缓存集合keep-alive inludes使用
if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
},
// 可删除指定的一个view
DEL_VISITED_VIEW(state, view) {
const i = state.visitedViews.indexOf(view)
if (i > -1) {
state.visitedViews.splice(i, 1)
}
},
// 可删除指定的一个view缓存
DEL_CACHED_VIEW(state, view) {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)
},
// 清空缓存列表
DEL_ALL_CACHED_VIEWS(state) {
state.cachedViews = []
}
}
// 定义actions
const actions: ActionTree<ITagsViewState, IRootState> = {
// 添加tags view
addView({ dispatch }, view: RouteRecordRaw) {
// 添加tag时也要判断该tag是否需要缓存
dispatch('addVisitedView', view)
dispatch('addCachedView', view)
},
// 添加可显示的tags view 添加前commit里需要进行去重过滤
addVisitedView({ commit }, view: RouteRecordRaw) {
commit('ADD_VISITED_VIEW', view)
},
// 添加可缓存的标签tag
addCachedView({ commit }, view: RouteRecordRaw) {
commit('ADD_CACHED_VIEW', view)
},
// 删除指定tags view 同时要把它从visitedViews和cachedViews中删除
delView({ dispatch }, view: RouteRecordRaw) {
return new Promise(resolve => {
// 删除对应显示的路由tag
dispatch('delVisitedView', view)
// 删除对应缓存的路由
dispatch('delCachedView', view)
resolve(null)
})
},
// 从可显示的集合中 删除tags view
delVisitedView({ commit }, view: RouteRecordRaw) {
commit('DEL_VISITED_VIEW', view)
},
// 从缓存列表删除指定tag view
delCachedView({ commit }, view: RouteRecordRaw) {
return new Promise(resolve => {
commit('DEL_CACHED_VIEW', view)
resolve(null)
})
},
// 清空缓存列表
delAllCachedViews({ commit }) {
commit('DEL_ALL_CACHED_VIEWS')
}
}
const tagsView: Module<ITagsViewState, IRootState> = {
namespaced: true,
state: {
visitedViews: [],
cachedViews: []
},
mutations,
actions
}
export default tagsView
5-2 AppMain中根据store中cachedViews列表进行缓存
之前我们是在AppMian中给了keep-alive的includes空数组,现在换成从store中获取cachedViews缓存列表
src/layout/components/AppMain.vue
<template>
<div class="app-main">
<!-- vue3 路由缓存 https://next.router.vuejs.org/guide/migration/index.html#router-view-keep-alive-and-transition -->
<router-view v-slot={Component}>
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" :key="key" />
</keep-alive>
</transition>
</router-view>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from '@/store'
export default defineComponent({
name: 'AppMain',
setup() {
const route = useRoute()
const store = useStore()
const key = computed(() => route.path)
// 缓存路由集合 暂时先是空数组
const cachedViews = computed(() => store.state.tagsView.cachedViews)
return {
key,
cachedViews
}
}
})
</script>
<style lang="scss" scoped>
.app-main {
/* navbar 50px */
min-height: calc(100vh - 50px);
}
.fade-transform-enter-active,
.fade-transform-leave-active {
transition: all .5s;
}
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
5-3 修改SizeSelect组件
SizeSelect组件主要用来切换element组件size,由于路由默认会被缓存,会导致动态修改了elemnt组件size不更新,所以需要在修改size后 清空缓存里列表 在刷新当前路由
<template>
<div>
<el-dropdown trigger="click" @command="handleSize">
<div>
<svg-icon class-name="size-icon" icon-class="size"></svg-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="item in sizeOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === size"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script lang="ts">
import { Size } from '@/plugins/element'
import {
defineComponent,
ref,
getCurrentInstance,
ComponentInternalInstance,
ComponentPublicInstance,
computed
} from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from '@/store'
import { nextTick } from 'process'
export default defineComponent({
name: 'SizeSelect',
setup() {
const store = useStore()
const route = useRoute()
const router = useRouter()
const { proxy } = getCurrentInstance() as ComponentInternalInstance
// store中获取size
const size = computed(() => store.getters.size)
// element size 选项
const sizeOptions = ref([
{ label: 'Default', value: 'default' },
{ label: 'Medium', value: 'medium' },
{ label: 'Small', value: 'small' },
{ label: 'Mini', value: 'mini' }
])
// 刷新当前路由
const refreshView = () => {
// 需要清除路由缓存 否则size配置改变后组件size状态被缓存不更新
store.dispatch('tagsView/delAllCachedViews')
const { fullPath } = route
nextTick(() => {
// 跳转到重定向中间页 实现当前路由刷新
router.replace({
path: '/redirect' + fullPath
})
})
}
// command 获取点击按钮的command属性值 作为size值
const handleSize = (command: Size) => {
// 修改element-plus组件尺寸
(proxy as ComponentPublicInstance).$ELEMENT.size = command
// 更新store
store.dispatch('app/setSize', command)
// 切换size需要刷新路由才能生效
refreshView()
proxy?.$message.success({
type: 'success',
message: 'Switch Size Success'
})
}
return {
sizeOptions,
size,
handleSize
}
}
})
</script>
<style lang="scss">
.size-icon {
font-size: 18px;
}
</style>
5-3 测试
本节参考源码
https://gitee.com/brolly/vue3-element-admin/commit/cb38b128c2a0b193d4ca42083b11e1c1b1c97870