Category | Component |
---|---|
Export Size | 734 B |
Last Changed | last week |
Define and reuse template inside the component scope.
It's common to have the need to reuse some part of the template. For example:
<template> <dialog v-if="showInDialog"> <!-- something complex --> </dialog> <div v-else> <!-- something complex --> </div> </template>
We'd like to reuse our code as much as possible. So normally we might need to extract those duplicated parts into a component. However, in a separated component you lose the ability to access the local bindings. Defining props and emits for them can be tedious sometime.
So this function is made to provide a way for defining and reusing templates inside the component scope.
In the previous example, we could refactor it to:
<script setup> import { createReusableTemplate } from '@vueuse/core' const [DefineTemplate, ReuseTemplate] = createReusableTemplate() </script> <template> <DefineTemplate> <!-- something complex --> </DefineTemplate> <dialog v-if="showInDialog"> <ReuseTemplate /> </dialog> <div v-else> <ReuseTemplate /> </div> </template>
<DefineTemplate>
will register the template and renders nothing.<ReuseTemplate>
will render the template provided by <DefineTemplate>
.<DefineTemplate>
must be used before <ReuseTemplate>
.Note: It's recommended to extract as separate components whenever possible. Abusing this function might lead to bad practices for your codebase.
You can also pass data to the template using slots:
v-slot="..."
to access the data on <DefineTemplate>
<ReuseTemplate>
to pass them to the template<script setup> import { createReusableTemplate } from '@vueuse/core' const [DefineTemplate, ReuseTemplate] = createReusableTemplate() </script> <template> <DefineTemplate v-slot="{ data, msg, anything }"> <div>{{ data }} passed from usage</div> </DefineTemplate> <ReuseTemplate :data="data" msg="The first usage" /> <ReuseTemplate :data="anotherData" msg="The second usage" /> <ReuseTemplate v-bind="{ data: something, msg: 'The third' }" /> </template>
createReusableTemplate
accepts a generic type to provide type support for the data passed to the template:
<script setup lang="ts"> import { createReusableTemplate } from '@vueuse/core' // Comes with pair of `DefineTemplate` and `ReuseTemplate` const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>() // You can create multiple reusable templates const [DefineBar, ReuseBar] = createReusableTemplate<{ items: string[] }>() </script> <template> <DefineFoo v-slot="{ msg }"> <!-- `msg` is typed as `string` --> <div>Hello {{ msg.toUpperCase() }}</div> </DefineFoo> <ReuseFoo msg="World" /> <!-- @ts-expect-error Type Error! --> <ReuseFoo :msg="1" /> </template>
Optionally, if you are not a fan of array destructuring, the following usages are also legal:
<script setup lang="ts"> import { createReusableTemplate } from '@vueuse/core' const { define: DefineFoo, reuse: ReuseFoo, } = createReusableTemplate<{ msg: string }>() </script> <template> <DefineFoo v-slot="{ msg }"> <div>Hello {{ msg.toUpperCase() }}</div> </DefineFoo> <ReuseFoo msg="World" /> </template>
<script setup lang="ts"> import { createReusableTemplate } from '@vueuse/core' const TemplateFoo = createReusableTemplate<{ msg: string }>() </script> <template> <TemplateFoo.define v-slot="{ msg }"> <div>Hello {{ msg.toUpperCase() }}</div> </TemplateFoo.define> <TemplateFoo.reuse msg="World" /> </template>
Dot notation is not supported in Vue 2.
It's also possible to pass slots back from <ReuseTemplate>
. You can access the slots on <DefineTemplate>
from $slots
:
<script setup> import { createReusableTemplate } from '@vueuse/core' const [DefineTemplate, ReuseTemplate] = createReusableTemplate() </script> <template> <DefineTemplate v-slot="{ $slots, otherProp }"> <div some-layout> <!-- To render the slot --> <component :is="$slots.default" /> </div> </DefineTemplate> <ReuseTemplate> <div>Some content</div> </ReuseTemplate> <ReuseTemplate> <div>Another content</div> </ReuseTemplate> </template>
Passing slots does not work in Vue 2.
This function is migrated from vue-reuse-template.
Existing Vue discussions/issues about reusing template:
Alternative Approaches:
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 type ReusableTemplatePair< Bindings extends object, Slots extends Record<string, Slot | undefined> > = [ DefineTemplateComponent<Bindings, Slots>, ReuseTemplateComponent<Bindings, Slots> ] & { define: DefineTemplateComponent<Bindings, Slots> reuse: ReuseTemplateComponent<Bindings, Slots> } /** * This function creates `define` and `reuse` components in pair, * It also allow to pass a generic to bind with type. * * @see https://vueuse.org/createReusableTemplate */ export declare function createReusableTemplate< Bindings extends object, Slots extends Record<string, Slot | undefined> = Record< string, Slot | undefined > >(): ReusableTemplatePair<Bindings, Slots>
© 2019-present Anthony Fu
Licensed under the MIT License.
https://vueuse.org/core/createReusableTemplate/