Skip to content
On this page

标签导航支持对应路由缓存

效果图 默认情况下没有 noCache 属性 或为 false 都会进行缓存 true 不缓存 image.pngimage.pngimage.png 再切回来 input 内容还在 image.png 如果为 true image.pngimage.pngimage.png 再切回来 input 内容没有了 image.png

右键删除某一个标签导航时也要删除对应路由缓存 效果 image.png 关闭后再打开 input 内容被清空

image.png

5-1 修改 store 创建缓存列表

再添加标签导航时,同时也判断该路由要不要缓存,要缓存就根据路由配置的 name 属性进行缓存(路由组件的 name 要与路由配置的 name 一致)再添加到 keep-alive inludes 的缓存列表中。 (keep-alive 内部是根据组件的 name 进行缓存,我们添加到 cachedViews 缓存列表的 name 是从每条路由配置的 name 取得值,所以路由组件和路由配置中必须要有一致的 name 属性。)

tagsView module

主要就是添加 cachedViews 缓存集合,再新增用来添加和删除 cachedViews 缓存列表 actions 和 muations

state image.pngimage.png actions (添加、删除) image.png mutations image.png src/store/modules/tagsView.ts

typescript
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

5-2 AppMain 中根据 store 中 cachedViews 列表进行缓存

之前我们是在 AppMian 中给了 keep-alive 的 includes 空数组,现在换成从 store 中获取 cachedViews 缓存列表

image.png src/layout/components/AppMain.vue

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>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

5-3 修改 SizeSelect 组件

SizeSelect 组件主要用来切换 element 组件 size,由于路由默认会被缓存,会导致动态修改了 elemnt 组件 size 不更新,所以需要在修改 size 后 清空缓存里列表 在刷新当前路由

image.png

vue
<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>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

5-3 测试

image.png 通过修改路由 noCache 属性来测试 image.png

本节参考源码

https://gitee.com/brolly/vue3-element-admin/commit/cb38b128c2a0b193d4ca42083b11e1c1b1c97870

沪ICP备20006251号-1