vue-router中多个路由引用同一异步组件的解决方案
得益于vue-router对异步组件的支持,我们在异步import的then回调中修改组件的name,这样每个组件实例就有唯一的缓存键值,正常被缓存、销毁,并且路由名称也不会冲突了。当你想使用路由name跳转到其中一个路由时,由于三个路由重名了,可能会导致不是预期的结果。路由重名vue-router会报警告。效果和方案1一样,只是路由不用重名了。
·
问题描述
- 使用过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)
}
}
路由配置:
实际效果:
经过测试此方案比较好用,有问题可评论留言
更多推荐
已为社区贡献5条内容
所有评论(0)