Multi-level route caching based on vue-element-Admin framework

rendering

1. Keep-alive route caching principle

Keep-alive caches the corresponding page component name property by route name as in the include array

The cachedViews array is maintained by store

<transition name="fade-transform" mode="out-in">
  <keep-alive :include="cachedViews">
    <router-view />
  </keep-alive>
</transition>
Copy the code

2. Route caching scheme

  • Routing in vue – element – admin is SRC/layout/component/AppMain files, only the primary routing, multilevel routing is to create a new empty routing file.

  • This scheme uses a single file to realize multilevel routing without writing another empty routing file.

The principle of

  • Keep-alive must be matched by the component name. The problem with using a component file to duplicate it is how to dynamically change the component name.

  • The component is configured on the Router, so it is difficult to rename the component. We have tried various methods to dynamically rename the component.

  • Child routing file Demo:

    import EmptyRoute from '@/layout/EmptyRoute' export default { path: '/lab', component: load('layout/index'), redirect: 'noRedirect', name: 'layout', alwaysShow: true, meta: {title: 'laboratory ',}, children: [{path: 'todo-list', Component: Load (' views/lab/todo/list/index '), name: 'lab - todo list', meta: {title: 'to-do'}}, {path: 'todo-list-detail/:id', component: load('views/lab/todo/list/detail'), name: 'lab-todo-list-detail', hidden: True, meta: {title: 'view to-do ', activeMenu: '/lab/todo-list',}}, {path: 'common', name: 'common', redirect: 'noRedirect',// forget to add component: {...EmptyRoute, name: 'common'},// Subroute alwaysShow: true, meta: {title: 'general requirements'}, children: [{path:' fairness ', component: the load (' views/lab/common/fairness index '), name: 'lab-common-fairness', meta: {title: 'fairness',}}, {path: 'privacy', name: 'privacy', redirect: 'noRedirect', Component: {...EmptyRoute, name: 'privacy'},// Subroute alwaysShow: true, meta: {title: 'privacy'}, children: [ { path: 'agreement', component: load('views/lab/common/privacy/agreement/index'), name: 'lab-common-privacy-agreement', meta: {title: 'confidentiality agreement',}}]}]}}Copy the code
  • Route file emptyroute.vue

    <template> <transition name="fade-transform" mode="out-in"> <keep-alive :include="cachedViews"> <router-view /> </keep-alive> </transition> </template> <script> // Configure whether to enable route caching import {needTagsView} from "@/ Settings "; export default { computed: CachedViews () {if (this.routename && needTagsView) {const cached = this.$store.getters.cached; const cachedViews = cached ? cached[this.routeName] : []; return cachedViews || []; } return []; }}, data() {return {// with routeName this is the default routeName instead of layout MainApp routeName: "layout"}; }, created () {/ / rename the routing here enclosing routeName = this. $options. The name | | "layout". }}; </script>Copy the code
  • Store tagsview.js transformation

    • Everything is based on visitedViews: Set the route name that should be cached for each level of route according to the matched array of each level of route in the array, which is stored in the cached object. The core method is setMatched, and the matched object uses the route name as the key value

  • code

/* eslint-disable no-shadow */ const state = {isRefresh: false,// whether cached: {}, visitedViews: [], } const mutations = {} function filterView(view) { if (! view) return view const { fullPath, name, path, meta, params, query, matched } = view return { fullPath, name, path, meta, params, query, matched: matched ? matched.map(i => ({ meta: i.meta, name: i.name, path: i.path, })) : undefined } } const actions = { retsetState({ state }) { state.visitedViews = [] state.cached = {} }, setMatched({ dispatch, state }) { const obj = {} state.visitedViews.forEach(view => { if (view.meta.affix && view.meta.matchedKey) { let arr = obj[view.meta.matchedKey] || [] if (! arr.includes(view.name)) { arr.push(view.name) } obj[view.meta.matchedKey] = arr } else if (view.matched && ! view.meta.noCache) { const matched = view.matched const len = matched.length if (len < 2) return for (let idx = 0; idx < matched.length; idx++) { const key = matched[idx].name; if (idx < len - 1) { const vnext = matched[idx + 1]; const { meta, name } = vnext if (meta && (meta.affix || ! meta.noCache)) { let arr = obj[key] || [] if (! arr.includes(name)) { arr.push(name) } obj[key] = arr } } } } }) state.cached = obj }, addView({ dispatch, state }, view) { try { if (state.visitedViews.some(v => v.path === view.path) && state.isRefresh===false) return state.isRefresh = false view = filterView(view) const idx = state.visitedViews.findIndex(v => v.name === view.name) if (idx > -1) { state.visitedViews.splice(idx, 1, { ... view, title: view.meta.title || '' }) } else { state.visitedViews.push( { ... view, title: view.meta.title || '' } ) } dispatch('setMatched') } catch (error) { console.log('addView', error); } }, delView({ dispatch, state }, view) { return new Promise(resolve => { const idx = state.visitedViews.findIndex(i => i.path === view.path) if (idx > -1) { state.visitedViews.splice(idx, 1) } dispatch('setMatched') resolve({ visitedViews: state.visitedViews }) }) }, refreshView({ dispatch, state }, view) { return new Promise(resolve => { let name = view.name let key = 'layout' if (view.matched) { const len = view.matched.length key = view.matched[len - 2].name } state.cached[key] = state.cached[key].filter(i => i ! == name) state.isRefresh = true resolve() }) }, delOthersViews({ dispatch, state }, view) { return new Promise(resolve => { let arr = state.visitedViews.filter(i => i.meta.affix) if (view && ! view.meta.affix) { arr.push(view) } state.visitedViews = arr dispatch('setMatched') resolve({ visitedViews: arr }) }) }, } export default { namespaced: true, state, mutations, actions }Copy the code
  • Layout TagsView component method changes: Call actions method changes
initTags() {
  this.affixTags = this.filterAffixTags(this.routes)
  for (const tag of this.affixTags) {
    // Must have tag name
    if (tag.name) {
      this.$store.dispatch('tagsView/addView', tag)
    }
  }
}
addTags() {
  const route = this.getActiveRoute(this.$route)
  const { name } = route
  if (name) {
    this.$store.dispatch('tagsView/addView', route)
  }
  return false
},
refreshSelectedTag(view) {
  this.$store.dispatch('tagsView/refreshView', view).then(() => {
    const { fullPath } = view
    this.$nextTick(() => {
      this.$router.replace({
        path: '/redirect' + fullPath
      })
    })
  })
},
closeSelectedTag(view) {
  if (view.meta && view.meta.affix) return
  this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
    if (this.isActive(view)) {
      this.toLastView(visitedViews, view)
    }
  })
},
closeOthersTags() {
  this.$router.push(this.selectedTag)
  this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
    this.moveToCurrentTag()
  })
},
closeAllTags(view) {
  this.$store.dispatch('tagsView/delOthersViews').then(({ visitedViews }) => {
    this.toLastView(visitedViews, view)
  })
},
Copy the code
  • Layout /index modification

    <section class="app-main"> <EmptyRoute></EmptyRoute> </sectionCopy the code

After the above transformation, you can happily write multi-level routing!!