插槽内容与出口#
- https://cn.vuejs.org/guide/components/slots.html#slot-content-and-outlet
- 子组件
FancyButton.vue
。
1
2
3
4
5
6
|
<template>
<button class="fancy-btn">
<!-- 插槽内容,等待传入的内容 -->
<slot></slot>
</button>
</template>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
|
<template>
<div>
<FancyButton>
这里是插槽内容
</FancyButton>
</div>
</template>
<script setup>
import FancyButton from './components/FancyButton.vue';
</script>
|
- 渲染后的结构。
<div>
<button class="fancy-btn">这里是插槽内容</button>
</div>
渲染作用域#
- https://cn.vuejs.org/guide/components/slots.html#render-scope
- 子组件
FancyButton.vue
。
1
2
3
4
5
|
<template>
<button class="fancy-btn">
<slot></slot>
</button>
</template>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<template>
<div>
<span>{{ message }}</span>
<FancyButton>
{{ message }}
</FancyButton>
</div>
</template>
<script setup>
import FancyButton from './components/FancyButton.vue';
import { ref } from 'vue'
const message = ref('hello, world!')
</script>
|
默认内容#
- https://cn.vuejs.org/guide/components/slots.html#fallback-content
- 子组件
SubmitButton.vue
。
1
2
3
4
5
6
7
8
9
10
11
|
<template>
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>
</template>
<script setup>
</script>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
|
<template>
<div>
<SubmitButton />
</div>
</template>
<script setup>
import SubmitButton from './components/SubmitButton.vue';
</script>
|
具名插槽#
- https://cn.vuejs.org/guide/components/slots.html#named-slots
- 子组件
BaseLayout.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<!-- 没有提供name的<slot>出口会隐式地命名为“default” -->
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script setup>
</script>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<template>
<div>
<BaseLayout>
<!-- v-slot:header 简写 #header -->
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
- 隐式的默认插槽。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<BaseLayout>
<template #header>
<h1>Here might be a page title</h1>
</template>
<!-- 隐式的默认插槽 -->
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template #footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
|
- 渲染结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<!-- footer标签会一直存在 -->
<footer>
<p>Here's some contact info</p>
</footer>
</div>
|
条件插槽#
- https://cn.vuejs.org/guide/components/slots.html#conditional-slots
- 子组件
BaseLayout.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<template>
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<div v-if="$slots.default" class="card-content">
<slot />
</div>
<!-- 如果不传该div标签不会存在 -->
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
<script setup>
</script>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<template>
<div>
<BaseLayout>
<!-- v-slot:header 简写 #header -->
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
动态插槽名#
- https://cn.vuejs.org/guide/components/slots.html#dynamic-slot-names
作用域插槽#
- https://cn.vuejs.org/guide/components/slots.html#scoped-slots
- 子组件
BaseLayout.vue
。
1
2
3
4
5
6
7
8
9
|
<template>
<div>
<slot :text="greetingMessage" :count="1"></slot>
</div>
</template>
<script setup>
const greetingMessage = 'hello'
</script>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
|
<template>
<div>
<BaseLayout v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
- 使用解构写法。
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- App.vue -->
<template>
<div>
<BaseLayout v-slot="{ text, count }">
{{ text }} {{ count }}
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
具名作用域插槽#
- https://cn.vuejs.org/guide/components/slots.html#named-scoped-slots
- 子组件
BaseLayout.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<template>
<div>
<BaseLayout>
<template #header="headerProps">
<!-- { "message": "hello" } -->
{{ headerProps }}
</template>
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
|
<template>
<div>
<!-- name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽 -->
<slot name="header" message="hello"></slot>
</div>
</template>
<script setup>
</script>
|
- 使用解构写法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!-- App.vue -->
<template>
<div>
<BaseLayout>
<template #header="{ message }">
{{ message }}
</template>
</BaseLayout>
</div>
</template>
<script setup>
import BaseLayout from './components/BaseLayout.vue';
</script>
|
高级列表组件示例#
- https://cn.vuejs.org/guide/components/slots.html#fancy-list-example
- 子组件
FancyList.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<template>
<ul>
<li v-if="!items.length">
Loading...
</li>
<li v-for="item in items">
<slot name="item" v-bind="item"/>
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps(['api-url', 'per-page'])
const items = ref([])
// mock remote data fetching
setTimeout(() => {
items.value = [
{ body: 'Scoped Slots Guide', username: 'Evan You', likes: 20 },
{ body: 'Vue Tutorial', username: 'Natalia Tepluhina', likes: 10 }
]
}, 1000)
</script>
<style scoped>
ul {
list-style-type: none;
padding: 5px;
background: linear-gradient(315deg, #42d392 25%, #647eff);
}
li {
padding: 5px 20px;
margin: 10px;
background: #fff;
}
</style>
|
- 父组件
App.vue
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<script setup>
import FancyList from './components/FancyList.vue';
</script>
<template>
<FancyList api-url="url" :per-page="10">
<template #item="{ body, username, likes }">
<div class="item">
<p>{{ body }}</p>
<p class="meta">by {{ username }} | {{ likes }} likes</p>
</div>
</template>
</FancyList>
</template>
<style scoped>
.meta {
font-size: 0.8em;
color: #42b883;
}
</style>
|
无渲染组件#
- https://cn.vuejs.org/guide/components/slots.html#renderless-components