跳到主要內容

createReusableTemplate

類別
導出大小
576 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 定義在組件 setup 之外,並傳遞到 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>

傳遞資料

您也可以使用插槽將資料傳遞到模板

  • 使用 v-slot="..." 來訪問 <DefineTemplate> 上的資料
  • 直接在 <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 的情況下傳遞布林值 props。有關更多詳細資訊,請參閱 注意事項 部分。

Props 和 Attributes

預設情況下,所有傳遞到 <ReuseTemplate> 的 props 和 attributes 都將傳遞到模板。如果您不希望某些 props 傳遞到 DOM,則需要定義 runtime props

ts
import { createReusableTemplate } from '@vueuse/core'

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

如果您不想將任何 props 傳遞到模板,則可以傳遞 inheritAttrs 選項

ts
import { createReusableTemplate } from '@vueuse/core'

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

傳遞插槽

也可以從 <ReuseTemplate> 傳遞插槽回來。您可以從 $slots 訪問 <DefineTemplate> 上的插槽

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>

注意事項

布林值 props

與 Vue 的行為相反,定義為 boolean 且在沒有 v-bind 或缺席的情況下傳遞的 props 將分別解析為空字串或 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<
  Props extends Record<string, any>,
> {
  /**
   * Inherit attrs from reuse component.
   *
   * @default true
   */
  inheritAttrs?: boolean
  /**
   * Props definition for reuse component.
   */
  props?: ComponentObjectPropsOptions<Props>
}
/**
 * This function creates `define` and `reuse` components in pair,
 * It also allow to pass a generic to bind with type.
 *
 * @see https://vueuse.dev.org.tw/createReusableTemplate
 */
export declare function createReusableTemplate<
  Bindings extends Record<string, any>,
  MapSlotNameToSlotProps extends
    ObjectLiteralWithPotentialObjectLiterals = Record<"default", undefined>,
>(
  options?: CreateReusableTemplateOptions<Bindings>,
): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps>

原始碼

原始碼文件

貢獻者

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

更新日誌

v12.6.0 on 2/14/2025
18031 - feat: 明確的 props (#4535)
v12.0.0-beta.1 on 11/21/2024
0a9ed - feat!: 移除 Vue 2 支援,優化捆綁包並清理 (#4349)
v10.8.0 on 2/20/2024
75168 - fix: 改善型別 (#3641)
a086e - fix: 更嚴格的型別
v10.3.0 on 7/30/2023
a32ae - feat: 繼承 attrs (#3226)
d79e1 - fix: 駝峰化 props (#3253)
v10.1.1 on 5/1/2023
b3323 - fix: 改善 hmr 支援
v10.0.0-beta.5 on 4/13/2023
13169 - feat(createTemplatePromise): 新函數 (#2957)
bd53c - feat: 新函數 (#2961)

在 MIT 許可證下發布。