vue 关于keep-alive的问题

我的路由是设计成两个大路由,然后分别有子路由的,类似于

routes:[
    {
        path:'/home',
        child:[
        {
            path:'/home/a',
        },
        {
            path:'/home/b',
        }
        ]
    },
    {
        path:'/other',
        child:[
        {
            path:'/home/c',
        },
        {
            path:'/home/d',
        }
        ]
    }
]

然后我在app.vue设置

<keep-alive>
      <router-view></router-view>
      </keep-alive>

发现keep-alive 不生效
但是我如果分别在两个大路由的根组件上设置keep-alive,a页面到b页面,c页面到d页面成功缓存,但是a页面到c页面是缓存不成功的,有哪位道友遇到类似情况的??

阅读 7.6k
3 个回答

如楼上所说, <keep-alive>只对直属的子组件有效,
要使a到c的缓存有效,可以参考这里的做法

简单说就是使a,b,c,d放到同一个嵌套的子<router-view>, 给这个<router-view><keep-alive>

官方说法:
注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。

注意看下是不是有v-for

造成这个问题的原因是: 多级路由嵌套。
具体分析: 假如一个后台管理系统,有一个main.vue是所有页面的框架, 里面有这样一段代码

          <keep-alive>
            <router-view v-if="$route.meta.keepAlive"></router-view>
          </keep-alive>
          <router-view v-if="!$route.meta.keepAlive"></router-view>

然后建立了一个父级菜单,页面是ChildView.vue, 里面的代码如下

<router-view></router-view>

假设这个父菜单里有两个子菜单,分布为 A和B, 然后A设置为缓存(keepAlive=true), B不缓存(keepAlive=false);
当点击A菜单的时候,系统缓存了ChildView.vue组件。
当点击B菜单的时候,由于B设置的不缓存,所以导致ChildView.vue组件被销毁
这就是keep-alive为什么会失效的根本原因。

还有更复杂的,比如同一个菜单里的子菜单可以缓存,但是点击另外一个父菜单或者父菜单下的子菜单却不缓存, 究其原理和上面的分析是一样的原因,就是多级菜单,多个共用组件导致的keepAlive缓存失效, keepAlive根本没考虑到页面缓存的复杂性。

分析问题:
既然是多个router-view嵌套并且共用的情况下造成的,那么如果只存在一个router-view,也就是只需要main.vue作为框架内所有页面的容器,就不会有这个问题。

实际上是不是多级菜单对于项目或者业务上来讲一点都不影响,只是界面显示上需要,让用户能更快点击到自己需要的功能页面而已。 既然这样的话,显示的菜单保留多级的,实际的router弄成一级, 将显示菜单和业务router分离开。

解决问题:
首先将配置好的多级router用vuex缓存起来,用作展示的菜单。
然后将router转换一下,转换成一级菜单,用addRoutes异步添加到router里面。

局部示例代码:

const formatRouter = (routes, newRoutes = []) => {
  routes.map(item => {
    if (item.children && item.children.length > 0) formatRouter(item.children, newRoutes);
    newRoutes.push(item);
  })
  return newRoutes;
}
let flatRoutes = formatRouter(routes);
router.addRoutes(flatRoutes);

然后面包屑导航要调整一下,大部分逻辑都是从route.matched里面获取的,但是现在router全是一级的,我们要从展示的菜单数据里面拿面包屑导航数据。

示例代码:

/**
 * 自定义查找字段啊, 根据最后一级某个字段查找完整树(整个父类)
 * @param {*} val     要查找对比的值
 * @param {*} data    要查找的数据
 * @param {*} fKey    要查找对比的字段
 */
export const recursiveTreeByLastLevel = (val, data, fKey = 'value') => {
  let rData = [];
  for (let i = 0, len = data.length; i < len; i++) {
    rData.push(data[i]);
    if (data[i].children && data[i].children.length > 0) {
      rData = rData.concat(recursiveTreeByLastLevel(val, data[i].children, fKey));
      if (rData.some(item => item[fKey] === val)) return rData;
    }
    if (data[i][fKey] === val) return rData;
    rData = [];
  }
  return rData;
}
 
router.afterEach((to, from, next) => {
  var routerList = recursiveTreeByLastLevel(to.name, store.state.sidebarMenu, 'name')
  store.commit('setCrumbList', routerList) // 通过vuex缓存
})

搞完,收工!

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题