VUE keep-alive 动态移除缓存页面

·  阅读 1743

如何使用keep-alive动态缓存页面

使用场景:

产品希望在用户在多个页面来回切换的时候,不要丢失查询的结果和填写的表单。以下提供两种解决方案。

常规方案:使用vuex配合excludeinclude

<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>
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改