问题描述

  • 使用过vue-router的都知道,keep-alive只根据组件名称去缓存,而通常我们会将路由name作为缓存键值,所以默认情况下会把组件名称=路由名称
  • 在某些场景,我们可能需要在多个路由页面复用同一组件,并根据传入的参数来呈现不同的内容,如果有一个组件名称为Test,有多个路由需要引用该如何实现呢?
    在这里插入图片描述

解决方案

方案1:路由名称与组件名称一致(路由重名)

组件名称:

在这里插入图片描述

路由配置:

在这里插入图片描述

路由重名vue-router会报警告

在这里插入图片描述

分别打开这几个路由,可以发现是不同的实例,keep-alive也可以正常的缓存组件实例:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

但是,在使用时会发现一些问题:

  • 在使用keep-alive去缓存组件时,三个实例的缓存键值都是"RouteUseOne",那么在关闭一个路由的时候,只有两种逻辑:

    • 将"RouteUseOne"从keep-alive的include绑定数据中移除,那么这三个实例都会被销毁。
    • "RouteUseOne"不移除(或者说只在最后一个实例关闭时移除):实例依然被缓存,路由关闭后再打开,用的是原来的实例,效果如下图
      在这里插入图片描述
  • 当你想使用路由name跳转到其中一个路由时,由于三个路由重名了,可能会导致不是预期的结果。

方案2:路由名称不同,在meta中指定缓存键值(路由不重名)

路由配置:
在这里插入图片描述
效果和方案1一样,只是路由不用重名了

方案3(推荐):异步import时,修改组件名称与路由名称一致

得益于vue-router对异步组件的支持,我们在异步import的then回调中修改组件的name,这样每个组件实例就有唯一的缓存键值,正常被缓存、销毁,并且路由名称也不会冲突了

注意以下几点:

  • 函数型组件不生效、不支持,因为函数型组件没有实例
  • 一定程度上会消耗内存,毕竟使用了解构

实现代码(更新兼容了同步组件):

/**
 * 动态路由组件
 * 由于keep-alive只根据组件name来判断是否缓存,所以需要用到此方法来解决一些场景问题(如多个路由使用同一组件)
 * 
 * @template {import('vue').ComponentOptions|Promise<import('vue').ComponentOptions>} T
 * @param {T} componentOptions
 * @param {string} routeName 路由名称
 * @returns {T}
 * 
 * @example
 * // 有时多个路由需要用到用一个组件,只是部分参数不一样
 * // 比如 路由: xxx/a 与 xxx/b 需要用到同一个组件(name为Test),此时可以这样:
 * // 路由 xxx/a
    {
      path: 'a',
      component: () => dynamicRouteComponent(import('xxx/Test'), 'A'),
      name: 'A',
      meta: {
        title: 'A',
        params: '...',
        ...
      }
    }

    // 路由 xxx/b
    {
      path: 'b',
      component: () => dynamicRouteComponent(import('xxx/Test'), 'B'),
      name: 'B',
      meta: {
        title: 'B',
        params: '...',
        ...
      }
    }
    
    // 如果需要使用require
    {
      path: 'b',
      component: (resolve) => require([`@/views/xxx`], (dependency) => resolve(dynamicRouteComponent(dependency, 'B'))),
      name: 'B',
      meta: {
        title: 'B',
        params: '...',
        ...
      }
    }
    // 或者
    {
      path: 'b',
      component: () => Promise.resolve(dynamicRouteComponent(require(`@/views/xxx`), 'B')),
      name: 'B',
      meta: {
        title: 'B',
        params: '...',
        ...
      }
    }
    // 在组件生命周期的中去捕获路由参数即可(如created事件)
 */
export function dynamicRouteComponent(componentOptions, routeName) {
  const clone = (comp) => {
    if (comp && comp.default) {
      return { ...comp.default, name: routeName }
    }
    return comp
  }

  if (componentOptions instanceof Promise) {
    // 异步组件
    return componentOptions.then((comp) => {
      return clone(comp)
    })
  } else {
    // 同步组件
    return clone(componentOptions)
  }
}

路由配置:

在这里插入图片描述

实际效果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

经过测试此方案比较好用,有问题可评论留言

Logo

前往低代码交流专区

更多推荐