基础

作用

插槽就是给子组件添加模板字段的方式,让插入的模板在子组件中进行渲染。

代码示例

1
2
3
4
5
6
7
8
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>

<!-- FancyButton组件内容 -->
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>

说明

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

作用域

插槽内容在哪个组件当中,那他使用的数据就是哪个组件的,跟他最后渲染到哪个组件里面没有任何关系。

  • 插槽内容可以访问父组件数据【因为插槽内容本来就在父组件定义】
  • 茶插槽内容不能访问子组件数据

插槽后备内容

slot标签内部添加内容,在没有给组件内部写内容时,插槽的默认内容为slot标签内部的内容。

具名插槽

对于单个子组件内部需要不同地方使用插槽,这个时候如果没有区分的话,那么每个插槽内部都会获取到该组件内部的所有内容,所以为了让不同的内容到对应的位置去,就有了具名插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 父组件 -->
<Card :content="content">
<div>社区小贴</div>
<div>测试数据测试数据</div>
<div>1235321478</div>
</Card>

<!-- 子组件 -->
<template>
<div class="">
<header><slot /></header>
<main><slot /></main>
<footer><slot /></footer>
</div>
</template>

这种情况就是每个标签内部都会显示出着三条内容。

  • 给子组件添加name属性:
1
2
3
4
5
6
7
8
9
<!-- 此时所有的内容都会显示在默认插槽内部,也就是下面的 <slot />内 -->
<template>
<div class="">
<slot />
<header><slot name="header" /></header>
<main><slot name="main" /></main>
<footer><slot name="footer" /></footer>
</div>
</template>
  • <slot />等同于<slot name="default"/>
  • 所以就是说所有的插槽都有自己的名字。
  • 将插槽内容放置到自己对应的内容下的方法:
1
2
3
4
5
6
<Card :content="content">
<template v-slot:header>社区小贴</template>
<template #main>测试数据测试数据</template>
<template #footer>1235321478</template>
<template #default>1235321478</template>
</Card>
  • 将标签改为template标签,然后使用v-slot就可以实现
  • 同样的也可以使用#+slot的name属性简写来实现

插槽props使用

先来实现一个小功能。对于一个列表,每条数据需要一个删除功能,之前的写法是:

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
<!-- 父组件 -->
<template>
<Lesson v-for="item in data" :key="item.id" :lesson="item" @del="del"></Lesson>
</template>
<script>
export default {
methods: {
del(lesson) {
const index = this.data.findIndex(l => l.id == lesson.id);

this.data.splice(index, 1);
}
}
}
</script>

<!-- 子组件 -->
<template>
<div>
{{ lesson.title }}
<button @click="del">删除</button>
</div>
</template>
<script>
export default {
props: ['lesson'],
emits: ['del'],
data() {
return {

}
},
methods: {
del() {
this.$emit('del', this.lesson);
}
}
</script>

现在使用插槽可以快速实现:

方式一

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
<!-- 父组件 -->
<template>
<Lesson v-for="item in data" :key="item.id" :lesson="item">
<button @click="del(item)">删除</button>
</Lesson>
</template>
<script>
export default {
methods: {
del(lesson) {
const index = this.data.findIndex(l => l.id == lesson.id);

this.data.splice(index, 1);
}
}
}
</script>

<!-- 子组件 -->
<template>
<div class="">
{{ lesson.title }}
<slot></slot>
</div>
</template>
  • 这种方式插槽内部可以直接使用父组件数据,不用来回传递方法。

方式二

  • 先来熟悉一下使用slot的props将子组件参数传递给父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 子组件 -->
<template>
<div class="">
{{ lesson.title }}
<slot content="name"></slot>
</div>
</template>

<!-- 父组件 -->
<Lesson v-for="item in data" :key="item.id" :lesson="item">
<template v-slot:default="slotProps">
{{ slotProps }} <!-- {"content": "name"} 可以拿到子组件slot标签上的属性值 -->
<button>删除</button>
</template>
</Lesson>

需要准确拿到对应属性值,可以定义多个变量:

1
2
3
4
5
6
7
<!-- 父组件 -->
<Lesson v-for="item in data" :key="item.id" :lesson="item">
<template v-slot:default="{content}">
{{ content }} <!-- name 可以拿到子组件slot标签上的content属性的值 -->
<button>删除</button>
</template>
</Lesson>
  • 回到实现删除功能
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
<!-- 父组件 -->
<Lesson v-for="item in data" :key="item.id" :lesson="item">
<template #default="{id}">
<button>删除</button>
</template>
</Lesson>
<script>
export default {
methods: {
del(id) {
const index = this.data.findIndex(l => l.id == id);

this.data.splice(index, 1);
}
}
}
</script>

<!-- 子组件 -->
<template>
<div>
{{ lesson.title }}
<slot :id="lesson.id"></slot>
</div>
</template>

独占默认插槽使用

对于上面的子组件只有一个默认插槽时,此时父组件可以简写为以下形式也能实现形同效果:

1
2
3
<Lesson v-for="item in data" :key="item.id" :lesson="item" #default="{id}">
<button>删除</button>
</Lesson>
  • 这种方式需要注意:
    • 子组件只有一个插槽;
    • 当有多个插槽时,父组件将不能再添加template标签,否则会报错
    • 当子组件有多个插槽时,父组件就只能使用上面两种方式。

插槽这种传递参数的方式在使用第三方库时是非常常用的一种功能。

__END__