Vue3 useReusableTemplate 优雅的复用模板代码
在Angular中有一个很好用的功能,即在不封装组件的情况下复用模板代码:
eg:
<ng-template let-title="title" #helloTemplate>
<h1>hello, {{title}}</h1>
</ng-template>
<ng-container *ngTemplateOutlet="helloTemplate;context: {title: 'cxk'}"></ng-container>
<ng-container *ngTemplateOutlet="helloTemplate;context: {title: 'world'}"></ng-container>
我们通过
ng-template
和ng-container
的ngTemplateOutlet
即可实现模板复用,非常的好用!!
那么在Vue3中如何实现如此神奇的功能呢?
我们将其封装成hooks /src/hooks/useReusableTemplate.ts
import type { DefineComponent, Slot } from 'vue'
import { defineComponent, shallowRef } from 'vue'
export type DefineTemplateComponent<
Bindings extends object,
Slots extends Record<string, Slot | undefined>
> = DefineComponent<{}> & {
new (): { $slots: { default(_: Bindings & { $slots: Slots }): any } }
}
export type ReuseTemplateComponent<
Bindings extends object,
Slots extends Record<string, Slot | undefined>
> = DefineComponent<Bindings> & {
new (): { $slots: Slots }
}
export function useReusableTemplate<
Bindings extends object,
Slots extends Record<string, Slot | undefined> = Record<
string,
Slot | undefined
>
>() {
const render = shallowRef<Slot | undefined>()
const define = defineComponent({
setup(_, { slots }) {
return () => {
render.value = slots.default
}
}
}) as DefineTemplateComponent<Bindings, Slots>
const reuse = defineComponent({
inheritAttrs: false,
setup(_, { attrs, slots }) {
return () => {
if (!render.value && process.env.NODE_ENV !== 'production')
throw new Error('Failed to find the definition of reusable template')
return render.value?.({ ...attrs, $slots: slots })
}
}
}) as ReuseTemplateComponent<Bindings, Slots>
return [define, reuse] as const
}
usage
<script setup lang="ts">
import { useReusableTemplate } from '@/hooks/useReusableTemplate'
import { ref } from 'vue'
const [DefineTemplate, ReuseTemplate] = useReusableTemplate<{ title: string }>()
const count = ref(0)
const handleClick = () => {
count.value += 1
}
</script>
<template>
<DefineTemplate v-slot="{ title }">
<el-button @click="handleClick">{{ title }} - {{ count }}</el-button>
</DefineTemplate>
<ReuseTemplate title="金" />
<ReuseTemplate title="木" />
<ReuseTemplate title="水" />
<ReuseTemplate title="火" />
<ReuseTemplate title="土" />
</template>
某些在不必封装组件的情况,就可实现复用模板代码了,太好用了!
这个创意来自antfu
在vueuse
中已经有这个hooks了: createReusableTemplate
为之则易,不为则难。