createReusableTemplate
コンポーネントスコープ内でテンプレートを定義して再利用します。
動機
テンプレートの一部を再利用する必要があることはよくあります。例えば
<template>
<dialog v-if="showInDialog">
<!-- something complex -->
</dialog>
<div v-else>
<!-- something complex -->
</div>
</template>
コードをできるだけ再利用したいと考えています。そのため、通常は重複した部分をコンポーネントに抽出する必要があります。しかし、個別のコンポーネントでは、ローカルバインディングにアクセスする機能を失います。それらに対してpropsとemitsを定義するのは、時には面倒です。
そのため、この関数は、コンポーネントスコープ内でテンプレートを定義および再利用する方法を提供するために作成されました。
使用方法
前の例では、次のようにリファクタリングできます。
<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>
はテンプレートを登録し、何もレンダリングしません。<ReuseTemplate>
は、<DefineTemplate>
によって提供されたテンプレートをレンダリングします。<DefineTemplate>
は<ReuseTemplate>
の前に使用する必要があります。
注記: 可能な限り個別のコンポーネントとして抽出することをお勧めします。この関数を乱用すると、コードベースの悪い慣習につながる可能性があります。
Options API
Options APIで使用する場合、テンプレートで使用するために、コンポーネントの設定の外側にcreateReusableTemplate
を定義し、components
オプションに渡す必要があります。
<script>
import { createReusableTemplate } from '@vueuse/core'
import { defineComponent } from 'vue'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
export default defineComponent({
components: {
DefineTemplate,
ReuseTemplate,
},
setup() {
// ...
},
})
</script>
<template>
<DefineTemplate v-slot="{ data, msg, anything }">
<div>{{ data }} passed from usage</div>
</DefineTemplate>
<ReuseTemplate :data="data" msg="The first usage" />
</template>
データの受け渡し
スロットを使用してテンプレートにデータを渡すこともできます。
<DefineTemplate>
でデータにアクセスするには、v-slot="..."
を使用します。<ReuseTemplate>
でデータを直接バインドして、テンプレートに渡します。
<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>
TypeScriptサポート
createReusableTemplate
はジェネリック型を受け入れ、テンプレートに渡されるデータの型サポートを提供します。
<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>
配列のデストラクチャリングが好みでない場合は、次の使用方法も有効です。
<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>
警告
v-bind
を使用せずにブール値のプロップを渡すことはサポートされていません。注意点セクションの詳細を参照してください。
スロットの受け渡し
<ReuseTemplate>
からスロットを返すことも可能です。<DefineTemplate>
の$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>
注意点
ブール値のプロップ
Vueの動作とは異なり、v-bind
を使用せずに渡された、または存在しないboolean
として定義されたプロップは、それぞれ空の文字列またはundefined
として解決されます。
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
value?: boolean
}>()
</script>
<template>
<DefineTemplate v-slot="{ value }">
{{ typeof value }}: {{ value }}
</DefineTemplate>
<ReuseTemplate :value="true" />
<!-- boolean: true -->
<ReuseTemplate :value="false" />
<!-- boolean: false -->
<ReuseTemplate value />
<!-- string: -->
<ReuseTemplate />
<!-- undefined: -->
</template>
参考文献
この関数は、vue-reuse-templateから移行されました。
テンプレートの再利用に関する既存のVueの議論/問題
代替アプローチ
型宣言
型宣言を表示
type ObjectLiteralWithPotentialObjectLiterals = Record<
string,
Record<string, any> | undefined
>
type GenerateSlotsFromSlotMap<
T extends ObjectLiteralWithPotentialObjectLiterals,
> = {
[K in keyof T]: Slot<T[K]>
}
export type DefineTemplateComponent<
Bindings extends Record<string, any>,
MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
> = DefineComponent & {
new (): {
$slots: {
default: (
_: Bindings & {
$slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>
},
) => any
}
}
}
export type ReuseTemplateComponent<
Bindings extends Record<string, any>,
MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
> = DefineComponent<Bindings> & {
new (): {
$slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>
}
}
export type ReusableTemplatePair<
Bindings extends Record<string, any>,
MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
> = [
DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>,
ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>,
] & {
define: DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>
reuse: ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>
}
export interface CreateReusableTemplateOptions {
/**
* Inherit attrs from reuse component.
*
* @default true
*/
inheritAttrs?: boolean
}
/**
* This function creates `define` and `reuse` components in pair,
* It also allow to pass a generic to bind with type.
*
* @see https://vueuse.dokyumento.jp/createReusableTemplate
*/
export declare function createReusableTemplate<
Bindings extends Record<string, any>,
MapSlotNameToSlotProps extends
ObjectLiteralWithPotentialObjectLiterals = Record<"default", undefined>,
>(
options?: CreateReusableTemplateOptions,
): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps>
ソース
貢献者
変更ログ
v12.0.0-beta.1
2024/11/21v10.8.0
2024/2/20a086e
- fix: より厳格な型v10.3.0
2023/7/30v10.1.1
2023/5/1b3323
- fix: HMRサポートの改善v10.0.0-beta.5
2023/4/13