createReusableTemplate

createReusableTemplate

コンポーネントのスコープ内でテンプレートを定義し再利用します。

動機

テンプレートの一部を再利用する必要があることはよくあります。例えば:

<template>
  <dialog v-if="showInDialog">
    <!-- 複雑な内容 -->
  </dialog>
  <div v-else>
    <!-- 複雑な内容 -->
  </div>
</template>

コードをできるだけ再利用したいと考えます。通常、これらの重複した部分をコンポーネントに抽出する必要があります。しかし、別のコンポーネントにすると、ローカルバインディングにアクセスする能力を失います。それらのために propsemits を定義するのは時に面倒です。

この関数は、コンポーネントスコープ内でテンプレートを定義し再利用する方法を提供するために作られました。

使用法

前述の例を次のようにリファクタリングできます:

<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'

const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>

<template>
  <DefineTemplate>
    <!-- 複雑な内容 -->
  </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 lang="ts">
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'

// `DefineTemplate` と `ReuseTemplate` のペアを提供
const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()

// 複数の再利用可能なテンプレートを作成可能
const [DefineBar, ReuseBar] = createReusableTemplate<{ items: string[] }>()
</script>

<template>
  <DefineFoo v-slot="{ msg }">
    {/* `msg` は `string` 型として扱われます */}
    <div>Hello {{ msg.toUpperCase() }}</div>
  </DefineFoo>

  <ReuseFoo msg="World" />

  {/* @ts-expect-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>

::: warning v-bind なしでのブール型のプロップスの受け渡しはサポートされていません。詳細は Caveats セクションを参照してください。 :::

プロップスと属性

デフォルトでは、<ReuseTemplate> に渡されたすべてのプロップスと属性はテンプレートに渡されます。特定のプロップスをDOMに渡したくない場合は、ランタイムプロップスを定義する必要があります:

import { createReusableTemplate } from '@vueuse/core'

const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
  props: {
    msg: String,
    enable: Boolean,
  }
})

テンプレートにプロップスを渡したくない場合は、inheritAttrs オプションを渡すことができます:

import { createReusableTemplate } from '@vueuse/core'

const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
  inheritAttrs: false,
})

スロットの受け渡し

<ReuseTemplate> からスロットを戻すことも可能です。<DefineTemplate>$slots からスロットにアクセスできます:

<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'

const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>

<template>
  <DefineTemplate v-slot="{ $slots, otherProp }">
    <div some-layout>
      <!-- スロットをレンダリングするために -->
      <component :is="$slots.default" />
    </div>
  </DefineTemplate>

  <ReuseTemplate>
    <div>Some content</div>
  </ReuseTemplate>
  <ReuseTemplate>
    <div>Another content</div>
  </ReuseTemplate>
</template>

注意点

ブール型プロップス

Vue の動作とは異なり、boolean として定義されたプロップスが v-bind なしで渡された場合、または存在しない場合、それぞれ空の文字列または 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 の議論/問題:

代替アプローチ: