iview-admin不同路由使用keep-alive分别缓存

原创
2019/08/13 17:45
阅读数 5.3K

1 问题描述

自从使用iview-admin这个框架以来,长久有一个问题始终困扰着我,就是每次载入新路由(vue-router)时,总是会重新读取请求数据,这就引出了两个很严重的问题:

  1. 请求重复,响应速度慢(前者一般是后者的原因);
  2. 每次切换路由时,组件中的data都会重置(这在逻辑上很要命,譬如开销售开单时,需要选择批次,这时你需要到进销存页面查询实际各仓库各批次的数据,查询后再返回时,会很郁闷的发现之前填写的数据都没了,等你再重写填写单据到批次选择那里时,很可能批次数据又忘了,本来应该提供方便的系统,反而造成了不便);

如何使组件内的data形成缓存保留下来,在开发这两三个月以来,始终是悬挂在我心头上的一个大问题.在各种搜索问题的结果中,最接近的答案似乎就是使用keep-alive,但实际使用并不顺利,中间遇到了各种各样的问题(这些问题,我会在第3点中加以说明),但总算得到了比较满意的结果.


2 解决方案

实际的解决方案,只有核心的两点:

  1. 修改iview-admin中的App.vue,代码如下(这里只列举相关代码):
<template>
  <div id="app">
    <keep-alive>
      <router-view :key="getRouteKey" v-if="$route.meta.keepAlive" ></router-view>
    </keep-alive>
    <router-view :key="getRouteKey" v-if="!$route.meta.keepAlive" >
      <!-- 这里是不被缓存的视图组件,比如 Edit! -->
    </router-view>
  </div>
</template>

<script>
import { saveStoreState, loadStoreState } from '@/store/data-manage'

export default {
  name: 'App',
  computed: {
    getRouteKey () {
      // 当前路由的唯一key
      return this.$route.name + (this.$route.params.id || '')
    }
  },
  created () {
    //此处代码与本文主题无关
  }
}
</script>
  1. 对router.js中需要被缓存的路由组件加上keepAlive:true,如下:
{
    path: 'ziliao-shangpin',
    name: 'shangpin',
    meta: {
      access: ['super_admin', 'admin', 'shangpin'],
      icon: 'md-funnel',
      title: '商品',
      //这里是要添加的属性,如果不设置,则默认为false
      keepAlive: true,
    },
    component: () => import('@/view/basic-data/product/product.vue')
},

关于以上两点,有以下说明:

  • <keep-alive>是控制缓存数据的核心,这是vue自带的设置,基础知识请参考这里.
  • 如果你直接使用<keep-alive>去包裹<router-view>,那么大概率会什么缓存都体现不到,这里的原因比较复杂,但关键就是需要设置key.我所在的项目将key设置为组件的name加参数中的id,如果有其他需求,也可对此进行额外的修改;

3 常见问题说明

这里主要将一些我在解决问题过程中遇到的各种各种的问题.

  1. 很多博文中,即使<keep-alive>中包裹的<router-view>没有设置key,也能正常的缓存,这是什么原因?
    答: 仔细看那些博文代码中component后面跟的内容,都是一个个具体的组件,而非像iview-admin这样后面跟着的,是一个函数式组件.
    以下是我的推测,在没有设定key的时候,keep-alive形成缓存是以具体组件的name为区别的.
    而都是函数式组件(且没有key)的情况下,就只能形成一个缓存区.
    譬如有三个路由组件a,b,c,其中a,b的keepAlive为true,c的为false.
跳转方式 结果
a->c->a或b->c->b 留下缓存
a->b或b->a 不会留下缓存(没来及留下)
a->c->b或b->c->a 会留下缓存,但留下的缓存是一模一样的
关于第3行,部分人可能不明白,稍微展开解释下,譬如a中data的someNum设为123后,以这种跳转方式跳转到b中,将b中data同名的someNum设为456,再以这种方式返回到a中,就会发现a中的someNum也变成了456, 这就证明了这种跳转方式,是建立在a和b共享缓存数据的基础上的.这样显然不符合逻辑要求.

当设置key后,就相当于设置了不同的缓存分区,使得keep-alive的缓存按照key的方式分开;

  1. key的设置方式,为何不能直接设为$route.name?
    这是因为当切换组件回来时,如果判定有缓存数据,组件就不会被重新创建,这不符合一种常见的逻辑,即从列表中跳转到单据主页面的组件,如果是两个不同的单据,显示内容应当不一致,不应当使用相同的缓存数据,所以这里再加上id借以区分;

4 更严谨的逻辑

以上的解决方案,只是解决了较为基础的情况,但有一些较为特殊的情况,需要区分何时使用缓存,何时不使用.总共有两大类:

  1. 其他人和前端操作导致数据库变化;
    ->仅凭前台必定无法监测到这类变化,要自动化实现,则需后台参与,相对来说比较困难和繁琐,一般建议是通过前台的刷新来实现;
  2. 一个组件的数据操作,影响到另一个组件的数据(仅考虑路由组件即可);
    ->理论上前台都可监测到,问题在于如何实现?
    ->个人认为比较好的设计方案,是通过一级链接网(这是一个自定义的新名词)实现;

一级链接网简要介绍(仅逻辑,技术上可考虑存储到vuex的store中):

  • 将每个route的name,链接到若干关联的routeName集合中,形成类似键值对的一级关系网;
  • 当作为键值对中的键所在路由发生变化时,查询出对应值的所在路由,发出信号清除其缓存数据;
  • 但仅影响到一级,不会继续呈树状一样的往下传递,否则可能会形成往复链,或者链接路由数据过多(这种就需要递归了),在效率和逻辑上都是不理想的.

关于使keep-alive失效的技术手段;

  1. 刷新或模拟刷新,弊端是会使所有路由的缓存数据都无效化;
  2. 将created的执行的方法,放在activated中,根据一键连接网判断是否需要执行;
  3. [待测试]同样利用一级连接网,直接令router.js中的keepAlive为false,加载完毕后再变更为true;
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部