import { type VNode, cloneVNode, createVNode, defineComponent, h, KeepAlive } from "vue" import { useRoute } from "vue-router" import { useTagsViewStore } from "@/store/modules/tags-view" interface CompConsumerProps { component: VNode } /** 定义 compMap 对象,用于存储路由名称和对应的组件 */ const compMap = new Map() /** * CompConsumer 组件 * 用法:替换 标签以及内部代码,变成: * 优点:缓存路由时只需写路由 Name,无需再写组件 Name * 缺点:当路由表有动态路由匹配时(指向同一个组件),会出现复用组件的情况(例如修改 /info/1 时 /info/2 也会跟着改变) */ export const CompConsumer = defineComponent( (props: CompConsumerProps) => { const tagsViewStore = useTagsViewStore() const route = useRoute() return () => { // 获取传入的组件 const component = props.component // 判断当前是否包含 name,如果不包含 name,那就直接处理掉 name if (!route.name) return component // 获取当前组件的名称 const compName = (component.type as any)?.name // 获取当前路由的名称 const routeName = route.name as string let Comp: VNode // 检查 compMap 中是否已经存在对应的组件 if (compMap.has(routeName)) { // 如果存在,则直接使用该组件进行渲染 Comp = compMap.get(routeName)! } else { // 如果不存在,则克隆传入的组件并创建一个新的组件,将其添加到 compMap 中 const node = cloneVNode(component) if (compName && compName === routeName) { ;(node.type as any).name = `__${compName}__CUSTOM_NAME` } // @ts-expect-error this is VNode Comp = defineComponent({ name: routeName, setup() { return () => node } }) compMap.set(routeName, Comp) } // 使用 createVNode 函数创建一个 KeepAlive 组件,并缓存 cachedViews 数组中对应的组件 return createVNode( KeepAlive, { include: tagsViewStore.cachedViews }, { default: () => h(Comp) } ) } }, { name: "CompConsumer", props: ["component"] } )