跳到主要内容

createReusableTemplate

分类
导出大小
593 B
上次更改
上个月

在组件作用域内定义和重用模板。

动机

通常需要重用模板的某些部分。例如

vue
<template>
  <
dialog
v-if="showInDialog">
<!-- something complex --> </
dialog
>
<
div
v-else>
<!-- something complex --> </
div
>
</template>

我们希望尽可能多地重用代码。因此,通常我们可能需要将这些重复的部分提取到一个组件中。但是,在分离的组件中,您将无法访问本地绑定。为它们定义 props 和 emits 有时会很繁琐。

因此,此函数旨在提供一种在组件作用域内定义和重用模板的方法。

用法

在前面的示例中,我们可以将其重构为

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> 提供的模板。
  • <DefineTemplate> 必须在 <ReuseTemplate> 之前使用。

注意:建议尽可能提取为单独的组件。滥用此函数可能会导致您的代码库出现不良实践。

选项式 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 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 支持

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 传递布尔 prop。有关更多详细信息,请参阅注意事项部分。

Props 和 Attributes

默认情况下,传递给 <ReuseTemplate> 的所有 props 和 attributes 都将传递给模板。如果您不希望某些 props 传递给 DOM,您需要定义运行时 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 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>

注意事项

布尔型 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 讨论/问题

替代方法

类型声明

显示类型声明
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>,
> { /** * 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.cn/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
Arthur Darkstone
Guspan Tanadi
shelton louis
Carlos Rodrigues
Issayah
Kasper Seweryn
小的的 DeDe
Maik Kowol
coderwei
Jean-Baptiste AUBRÉE
Andrej Hýll

更新日志

5fd3a - fix: 更新 createReusableTemplate 和 createTemplateP... 的返回类型 (#4962)
v13.6.0
d32f8 - refactor: 为所有纯函数添加 @__NO_SIDE_EFFECTS__ 注释 (#4907)
v12.6.0
18031 - feat: 显式 props (#4535)
0a9ed - feat!: 放弃对 Vue 2 的支持,优化打包并清理 (#4349)
v10.8.0
75168 - fix: 改进类型 (#3641)
a086e - fix: 更严格的类型
v10.3.0
a32ae - feat: 继承 attrs (#3226)
d79e1 - fix: camelize props (#3253)
v10.1.1
b3323 - fix: 改进 hmr 支持

根据 MIT 许可证发布。