createReusableTemplate
createReusableTemplate 函数允许您在单个 Vue 组件内部定义和重用模板片段,而无需将其提取为单独的子组件。
动机
在 Vue 组件中,经常需要 重复使用某些模板部分。例如:
<template>
<dialog v-if="showInDialog">
<!-- something complex -->
</dialog>
<div v-else>
<!-- something complex -->
</div>
</template>
我们希望尽可能地重用代码。通常,我们可能会将这些重复的部分提取到单独的组件中。但是,在独立的组件中,您会 失去访问局部绑定(即父组件数据和方法)的能力。为它们定义 props 和 emits 有时会很繁琐。
因此,createReusableTemplate 函数旨在提供一种 在组件作用域内定义和重用模板 的方法。
用法
在上面的示例中,我们可以将其重构为:
<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 一起使用时,您需要 在组件 setup 之外定义 createReusableTemplate,并将其传递给 components 选项,以便在模板中使用它们。
<script>
import { createReusableTemplate } from '@vueuse/core'
import { defineComponent } from 'vue'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
export default defineComponent({
components: {
DefineTemplate,
ReuseTemplate,
},
setup() {
// ...
const data = 'Hello from parent'; // 示例数据
return { data };
},
})
</script>
<template>
<DefineTemplate v-slot="{ data, msg, anything }">
<div>{{ data }} passed from usage</div>
</DefineTemplate>
<ReuseTemplate :data="data" msg="The first usage" />
</template>
传递数据
您还可以使用 slots 将数据传递到模板:
- 使用
v-slot="..."来访问<DefineTemplate>上的数据。 - 直接在
<ReuseTemplate>上绑定数据以将其传递给模板。
<script setup>
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
const data = 'Hello';
const anotherData = 'World';
const something = 'VueUse';
</script>
<template>
<DefineTemplate v-slot="{ data, msg, anything }">
<div>{{ data }} passed from usage, message: {{ msg }}</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 }">
<div>Hello {{ msg.toUpperCase() }}</div>
</DefineFoo>
<ReuseFoo msg="World" />
<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>
Props 和 Attributes
默认情况下,传递给 <ReuseTemplate> 的所有 props 和 attributes 都将传递给模板。如果您不希望某些 props 传递到 DOM,您需要定义运行时 props:
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
props: {
msg: String,
enable: Boolean,
},
})
如果您不想向模板传递任何 props,您可以传递 inheritAttrs 选项:
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
inheritAttrs: false,
})
传递 Slots
也可以从 <ReuseTemplate> 中传回 slots。您可以通过 $slots 访问 <DefineTemplate> 上的 slots:
<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>Some content</div>
</ReuseTemplate>
<ReuseTemplate>
<div>Another content</div>
</ReuseTemplate>
</template>
注意事项
布尔值 Props
与 Vue 的行为不同,定义为布尔值且未通过 v-bind 传递或缺失的 props 将分别解析为空字符串或 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<
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.org/createReusableTemplate
*/
export declare function createReusableTemplate<
Bindings extends Record<string, any>,
MapSlotNameToSlotProps extends
ObjectLiteralWithPotentialObjectLiterals = Record<"default", undefined>,
>(
options?: CreateReusableTemplateOptions<Bindings>,
): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps>