主题
createReusableTemplate
在组件范围内定义和重用模板。
🌐 Define and reuse template inside the component scope.
动机
🌐 Motivation
通常会有需要重用模板某些部分的情况。例如:
🌐 It's common to have the need to reuse some part of the template. For example:
vue
<template>
<dialog v-if="showInDialog">
<!-- something complex -->
</dialog>
<div v-else>
<!-- something complex -->
</div>
</template>我们希望尽可能多地重用我们的代码。因此,通常我们可能需要将那些重复的部分提取到一个组件中。然而,在一个独立的组件中,你会失去访问本地绑定的能力。为它们定义 props 和 emits 有时可能很麻烦。
🌐 We'd like to reuse our code as much as possible. So normally we might need to extract those duplicated parts into a component. However, in a separated component you lose the ability to access the local bindings. Defining props and emits for them can be tedious sometimes.
因此,该函数旨在提供一种在组件范围内定义和重用模板的方法。
🌐 So this function is made to provide a way for defining and reusing templates inside the component scope.
用法
🌐 Usage
在前面的示例中,我们可以将其重构为:
🌐 In the previous example, we could refactor it to:
vue
<script setup lang="ts">
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>提供的模板。<ReuseTemplate>之前必须使用<DefineTemplate>。
注意:建议尽可能将其提取为独立组件。滥用此功能可能会导致代码库出现不良实践。
选项 API
🌐 Options API
在使用 Options API 时,你需要在组件 setup 之外定义 createReusableTemplate,并传递给 components 选项,以便在模板中使用它们。
🌐 When using with Options API, you will need to define createReusableTemplate outside of the component setup and pass to the components option in order to use them in the template.
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>传递数据
🌐 Passing Data
你还可以使用槽将数据传递到模板:
🌐 You can also pass data to the template using slots:
- 使用
v-slot="..."来访问<DefineTemplate>上的数据 - 直接将
<ReuseTemplate>上的数据绑定以传递给模板
vue
<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 支持
🌐 TypeScript Support
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>或者,如果你不喜欢数组解构,以下用法也是合法的:
🌐 Optionally, if you are not a fan of array destructuring, the following usages are also legal:
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
不支持在没有 v-bind 的情况下传递布尔属性。更多详情请参见 注意事项 部分。
参数和属性
🌐 Props and Attributes
默认情况下,传递给 <ReuseTemplate> 的所有 props 和属性都会传递给模板。如果你不希望某些 props 被传递到 DOM,你需要定义运行时 props:
🌐 By default, all props and attributes passed to <ReuseTemplate> will be passed to the template. If you don't want certain props to be passed to the DOM, you need to define the runtime props:
ts
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
props: {
msg: String,
enable: Boolean,
}
})如果你不想传递任何 props 给模板,你可以传递 inheritAttrs 选项:
🌐 If you don't want to pass any props to the template, you can pass the inheritAttrs option:
ts
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate({
inheritAttrs: false,
})传递插槽
🌐 Passing Slots
也可以从 <ReuseTemplate> 传回插槽。你可以从 $slots 访问 <DefineTemplate> 上的插槽:
🌐 It's also possible to pass slots back from <ReuseTemplate>. You can access the slots on <DefineTemplate> from $slots:
vue
<script setup lang="ts">
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>注意事项
🌐 Caveats
布尔属性
🌐 Boolean props
与 Vue 的行为相反,定义为 boolean 的 props 如果在传递时没有 v-bind 或者没有传递,将分别解析为空字符串或 undefined:
🌐 As opposed to Vue's behavior, props defined as boolean that were passed without v-bind or absent will be resolved into an empty string or undefined respectively:
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>参考
🌐 References
该函数已从 vue-reuse-template 迁移。
🌐 This function is migrated from vue-reuse-template.
关于重用模板的现有 Vue 讨论/问题:
🌐 Existing Vue discussions/issues about reusing template:
替代方法:
🌐 Alternative Approaches: