Quand on utilise vue
, on aime souvent faire des wrappers
autour de composants existants.
Pour faire ça bien, il faut faire suivre principalement 4 choses:
Pour le reste de cet article, on prendra en exemple 2 composants: Wrapped
et Wrapper
.
Comme son nom l’indique, le Wrapper
va envelopper Wrapped
et faire suivre tout ce qu’il faut.
Commençons d’abord par voir à quoi ressemble Wrapped
, le composant que l’on souhaite envelopper:
<template>
<div :style="{color: props.color}">
<div>
<slot name="header"></slot>
</div>
<slot></slot>
<div>
<button @click="emit('validate')">Validate</button>
</div>
</div>
</template>
<script lang="ts" setup>
const emit = defineEmits<{
validate: [];
}>();
const props = defineProps<{
color?: string;
}>()
</script>
Comme on peut le voir, ce composant possède 2 slots. Un slot nommé header
et le slot par défaut. Il émet un événement validate
et accepte une propriété color
.
<template>
<Wrapped v-bind="{ ...props, ...attrs }" v-on="emit">
<template v-for="(_, name) in slots" v-slot:[name]="slotData"
><slot :name="name" v-bind="slotData"
/></template>
</Wrapped>
</template>
<script lang="ts" setup>
import { useAttrs } from "vue";
import type {
ComponentProps,
ComponentSlots,
ComponentEmit
} from "vue-component-type-helpers";
import Wrapped from "./Wrapped.vue";
type Slots = ComponentSlots<typeof Wrapped>;
type Props = ComponentProps<typeof Wrapped>;
type Emit = ComponentEmit<typeof Wrapped>;
const props = defineProps<Props>();
const emit = defineEmits<Emit>();
const slots = defineSlots<Slots>();
const attrs = useAttrs();
</script>
Il existe toutefois des limites à cette solution. On pourrait faire en sorte également de faire suivre les fonctions exposées par les composants. Dîtes moi en commentaire si vous avez besoin de faire suivre ça! ⬇️
https://github.com/vuejs/language-tools/blob/master/packages/component-type-helpers/index.ts
https://gist.github.com/loilo/73c55ed04917ecf5d682ec70a2a1b8e2