Skip to content

createReusableTemplate

类别
导出体积
738 B
上次更改
2 minutes ago

在组件作用域内定义并复用模板。

动机

在开发中,经常有复用模板部分的需求。例如:

vue
<template>
  <dialog v-if="showInDialog">
    <!-- 复杂内容 -->
  </dialog>
  <div v-else>
    <!-- 复杂内容 -->
  </div>
</template>

我们希望尽可能多地复用代码。通常,我们会将这些重复的部分提取到一个独立的组件中。然而,在分离的组件中,你失去了直接访问局部绑定的能力。为它们定义 props 和 emits 有时会显得繁琐。

因此,这个函数提供了一种方式,在组件内部定义并复用模板。

使用方法

针对上述示例,我们可以这样重构:

vue
<script setup>
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> 之前使用。

注意:尽可能将逻辑提取为独立组件是推荐的做法。过度使用此功能可能导致代码结构不佳。

选项式 API

当与选项式 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 }} 从使用处传递而来</div>
  </DefineTemplate>

  <ReuseTemplate :data="data" msg="第一次使用" />
</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 }} 从使用处传递</div>
  </DefineTemplate>

  <ReuseTemplate :data="data" msg="第一次使用" />
  <ReuseTemplate :data="anotherData" msg="第二次使用" />
  <ReuseTemplate v-bind="{ data: something, msg: '第三次' }" />
</template>

TypeScript 支持

createReusableTemplate 接受泛型类型来为传递给模板的数据提供类型支持:

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

另外,如果你不喜欢数组解构,以下用法也是合法的:

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>

WARNING

在 Vue 2 中不支持点语法。

WARNING

传递布尔类型的 props 而不使用 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>
      <!-- 渲染插槽 -->
      <component :is="$slots.default" />
    </div>
  </DefineTemplate>

  <ReuseTemplate>
    <div>某些内容</div>
  </ReuseTemplate>
  <ReuseTemplate>
    <div>另一些内容</div>
  </ReuseTemplate>
</template>

WARNING

在 Vue 2 中,传递插槽不生效。

注意事项

Boolean props

与 Vue 默认行为不同,未使用 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 {
  /**
   * 从重用组件中继承属性。
   *
   * @default true
   */
  inheritAttrs?: boolean
}
/**
 * 此函数创建一对 `define` 和 `reuse` 组件,
 * 它还允许传递一个泛型来绑定类型。
 *
 * @see https://vueuse.org/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

更新日志

v10.8.0 on 2/20/2024
75168 - fix: improve types (#3641)
a086e - fix: stricter types
v10.3.0 on 7/30/2023
a32ae - feat: inherit attrs (#3226)
d79e1 - fix: camelize props (#3253)
v10.1.1 on 5/1/2023
b3323 - fix: improve hmr support
v10.0.0-beta.5 on 4/13/2023
13169 - feat(createTemplatePromise): new function (#2957)
bd53c - feat: new function (#2961)