コンテンツへスキップ

createReusableTemplate

カテゴリ
エクスポートサイズ
738 B
最終変更日
先週

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

動機

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

vue
<template>
  <dialog v-if="showInDialog">
    <!-- something complex -->
  </dialog>
  <div v-else>
    <!-- something complex -->
  </div>
</template>

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

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

使用方法

前の例では、次のようにリファクタリングできます。

vue
<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オプションに渡す必要があります。

vue
<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>でデータを直接バインドして、テンプレートに渡します。
vue
<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はジェネリック型を受け入れ、テンプレートに渡されるデータの型サポートを提供します。

vue
<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>

配列のデストラクチャリングが好みでない場合は、次の使用方法も有効です。

vue
<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>
vue
<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からスロットにアクセスできます。

vue
<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として解決されます。

vue
<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の議論/問題

代替アプローチ

型宣言

型宣言を表示
typescript
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>

ソース

ソースドキュメント

貢献者

Anthony Fu
Anthony Fu
shelton louis
Carlos Rodrigues
Issayah
Kasper Seweryn
小的的 DeDe
Maik Kowol
coderwei
Jean-Baptiste AUBRÉE
Andrej Hýll

変更ログ

v12.0.0-beta.1 2024/11/21
0a9ed - feat!: Vue 2サポートの削除、バンドルの最適化、クリーンアップ (#4349)
v10.8.0 2024/2/20
75168 - fix: 型の改善 (#3641)
a086e - fix: より厳格な型
v10.3.0 2023/7/30
a32ae - feat: 属性の継承 (#3226)
d79e1 - fix: プロパティのキャメルケース化 (#3253)
v10.1.1 2023/5/1
b3323 - fix: HMRサポートの改善
v10.0.0-beta.5 2023/4/13
13169 - feat(createTemplatePromise): 新しい関数 (#2957)
bd53c - feat: 新しい関数 (#2961)

MITライセンスの下でリリースされています。