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-templateng-containerngTemplateOutlet即可实现模板复用,非常的好用!!

那么在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

posted @ 2023-05-24 11:26  demo_you  阅读(566)  评论(0编辑  收藏  举报