Skip to content

createReusableTemplate

类别
导出体积
593 B
上次更改
2 months ago

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

动机

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

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

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

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

使用方法

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

vue
<script setup lang="ts">
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 lang="ts">
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

不支持在不使用 v-bind 的情况下传递布尔属性。有关更多详细信息,请参见 注意事项 部分。

属性和属性

默认情况下,传递给 <ReuseTemplate> 的所有属性和属性将传递给模板。如果您不希望某些属性传递到 DOM,您需要定义运行时属性:

ts
import { 
createReusableTemplate
} from '@vueuse/core'
const [
DefineTemplate
,
ReuseTemplate
] =
createReusableTemplate
({
props
: {
msg
:
String
,
enable
:
Boolean
,
} })

如果您不想将任何属性传递给模板,可以传递 inheritAttrs 选项:

ts
import { 
createReusableTemplate
} from '@vueuse/core'
const [
DefineTemplate
,
ReuseTemplate
] =
createReusableTemplate
({
inheritAttrs
: false,
})

传递插槽

也可以从 <ReuseTemplate> 返回插槽。您可以通过 $slots<DefineTemplate> 中访问这些插槽:

vue
<script setup lang="ts">
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>

注意事项

布尔属性

与 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 讨论议题:

替代方案:

类型声明

显示类型声明
ts
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>,
> { /** * 从重用组件中继承属性。 * * @default true */
inheritAttrs
?: boolean
/** * Props definition for reuse component. */
props
?:
ComponentObjectPropsOptions
<
Props
>
} /** * 此函数创建一对 `define` 和 `reuse` 组件, * 它还允许传递一个泛型来绑定类型。 * * @see https://vueuse.org/createReusableTemplate * * @__NO_SIDE_EFFECTS__ */ export declare function
createReusableTemplate
<
Bindings
extends
Record
<string, any>,
MapSlotNameToSlotProps
extends
ObjectLiteralWithPotentialObjectLiterals
=
Record
<"default", undefined>,
>(
options
?:
CreateReusableTemplateOptions
<
Bindings
>,
):
ReusableTemplatePair
<
Bindings
,
MapSlotNameToSlotProps
>

源码

源码文档

贡献者

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

更新日志

d32f8 - refactor: add @__NO_SIDE_EFFECTS__ annotations to all pure functions (#4907)
18031 - feat: explicit props (#4535)
0a9ed - feat!: drop Vue 2 support, optimize bundles and clean up (#4349)
75168 - fix: improve types (#3641)
a086e - fix: stricter types
a32ae - feat: inherit attrs (#3226)
d79e1 - fix: camelize props (#3253)