Vue——插槽
一、插槽的基本使用
插槽的作用:
Vue插槽是Vue中常见的一种组件间的相互通信方式
,作用是让父组件可以向子组件指定位置插入html结构,适用于父组件===>子组件,在要接收数据的组件页面通过<slot>
标签来表示,简单来说,就是通过此标签来起到占位的作用,而要插入的内容也会对应到标签所在的位置。
插槽的基本使用:
这里我们选定App.vue
作为父组件,Card.vue
作为子组件,作为演示插槽基本使用的代码文件。
如下:
App.vue
:
<template>
<div>
<Card>
<div>这是插槽插入的内容</div>
</Card>
------------------------
<Card> </Card>
</div>
</template>
<script>
import Card from "./***ponents/Card.vue";
export default {
***ponents: { Card },
};
</script>
Card.vue
:
<template>
<div>插槽的基本使用</div>
<div style="color: red">
<slot>没有插入内容时显示的默认数据</slot>
</div>
<!-- 可以插入多个 -->
<slot>没有插入内容时显示的默认数据</slot>
<slot>没有插入内容时显示的默认数据</slot>
</template>
<script>
export default {};
</script>
运行结果:
从上面的运行结果可以看到,插槽的基本使用
是子组件自身内容正常显示,插入的内容
是父组件向子组件插入的内容,并且插入的内容可以显示多条,如果没有插入内容,将显示<slot>
标签内的默认数据,另外<slot>
标签外也可以另外用其他html标签
包裹并增加样式。
总结:
-
<slot>
标签用于接收并展示父组件插入的内容。 -
<slot>
标签可以重复使用。 -
<slot>
标签外可以使用其他html标签
包裹,与其他html标签
没有很大区别。 -
<slot>
标签内可以插入默认显示的内容,当插槽内容为空时显示。
二、插槽作用域
插槽无论写在哪个父组件中,它所能使用的方法和属性就是该父组件中的方法和属性,跟插槽最终放置在的子组件没有关系,子组件若要使用父组件中的方法和属性,可以通过插槽绑定属性和方法,子组件内使用props
接收。下面我们通过代码了体会。
App.vue
:
<template>
<div>
<Card :content="content">
<button @click="show">{{ content }}</button>
</Card>
</div>
</template>
<script>
import Card from "./***ponents/Card.vue";
export default {
***ponents: { Card },
data() {
return {
content: "父组件的数据",
};
},
methods: {
show() {
console.log("父组件的show");
},
},
};
Card.vue
:
<template>
<h2>插槽的作用域</h2>
<slot>没有插入内容时显示的默认数据</slot>
<div>{{ content }}</div>
</template>
<script>
export default {
props: ["content"],
methods: {
show() {
console.log("子组件的show");
},
},
};
</script>
运行结果:
可以看到,父组件和子组件具有相同的方法show()
,点击父组件的数据
按钮,输出的是父组件中show()
方法的结果,子组件通过props
成功获取父组件的content
数据并展示。
总结:
- 插槽在哪个父组件,插槽所能使用的方法和属性就是该父组件的方法和属性。
- 子组件要使用父组件的属性和方法可以通过插槽绑定属性和方法,使用
props
接收。
三、具名插槽
简单来说,所谓的具名插槽就是具有名字的插槽
那它和默认插槽的区别在哪呢?
我们经常会遇到这样的场景,多个类似的结构复用同一个组件,但是这些结构又有一些不同。如下图,左侧和右侧的结构复用同一个组件,可以看到左上角的内容以及下方的结构是不同的,左上角的标题我们通过普通插槽传入不同的数据可以实现展示不同标题,但是下面的主体结构如果还使用普通插槽的话,那么将会呈现相同的结构;这时,我们就需要具名插槽了,根据不同的结构,定义不同的插槽,并赋予名字,以便于调用。
下面我们通过代码来理解什么是具名插槽以及具名插槽如何使用
App.vue
:
<template>
<div>
<Card>
<template v-slot:header>
<div>
这是具名插槽header,想放啥就放啥,很灵活
<div
style="background-color: #ddd; width: 100px; height: 100px"
></div>
</div>
</template>
<template #main>
<div>这是具名插槽main,想放啥就放啥,很灵活</div>
<div style="background-color: #000; width: 100px; height: 100px"></div>
</template>
</Card>
--------------------------------------------------------
<Card>
<template v-slot:header>
<div>这是具名插槽header,想放啥就放啥,很灵活</div>
</template>
<template #main>
<div>这是具名插槽main,想放啥就放啥,很灵活</div>
</template>
<template #footer>
<div>这是具名插槽footer,想放啥就放啥,很灵活</div>
</template>
</Card>
---------------------------------------------------------
<Card>
<template v-slot:header>
<div>这是具名插槽header</div>
</template>
<template #main>
<div>这是具名插槽main</div>
</template>
<template #footer>
<div>这是具名插槽footer</div>
</template>
</Card>
</div>
</template>
<script>
import Card from "./***ponents/Card.vue";
export default {
***ponents: { Card },
};
Card.vue
:
<template>
<div class="card">
<header>
<slot name="header">默认数据</slot>
</header>
<main>
<slot name="main">默认数据</slot>
</main>
<footer>
<slot name="footer">默认数据</slot>
</footer>
</div>
</template>
<script>
export default {};
</script>
<style scoped>
.card {
border: solid 1px #ddd;
}
header,
main,
footer {
padding: 10px;
}
main {
border-width: 1px 0 1px 0;
border-style: solid;
background-color: #ddd;
}
</style>
运行结果:
可以看到根据需求不同,使用具名插槽可以传入不同的结构,提升了组件使用的灵活性。
总结:
- 父组件使用
<template v-slot:插槽名>插入的内容<template />
或<template #插槽名>插入的内容<template />
定义插槽,子组件使用<slot name="插槽名"><slot/>
来接收插槽。 - 默认插槽也是有名字的,
<slot name="default">
。 - 具名插槽提升了组件使用的灵活性。
四、插槽props实例操作[使用UI组件库时常用]
需求:实现渲染电影数据,并根据id删除数据。
为了简化操作,我们自定义一条数据如下movies.js
,其中包括四条电影数据。
movies.js
export default [
{ id: 1, title: "Scent of a Women" },
{ id: 2, title: "Gone with the Wind" },
{ id: 3, title: "Titanic" },
{ id: 4, title: "Heidi" },
];
我们在父组件App.vue
中循环使用插槽的方式在子组件Movie.vue
中展示四条电影数据,并绑定单条数据movie
,将其传递给子组件Movie
,子组件Movie
使用props
接收单条电影数据movie
。
App.vue
<template>
<Movie v-for="movie of movies" :key="movie.id" :movie="movie">
<template v-slot="{ id }">
<button @click="del(id)">删除</button>
</template>
</Movie>
</template>
<script>
import movies from "./movies";
import Movie from "./***ponents/Movie.vue";
export default {
***ponents: { Movie },
data() {
return {
movies,
};
},
methods: {
del(id) {
const index = this.movies.findIndex((item) => item.id === id);
this.movies.splice(index, 1);
},
},
};
</script>
要实现根据id
删除对应电影数据,需要在子组件Movie
中使用<slot :id="属性名">
的方式绑定数据,这样父组件App
则可以使用v-slot:插槽名="任意字符"
接收子组件传递过来的id,再根据id
调用del()
方法实现删除操作。
Movie.vue
<template>
<div class="movie">
{{ movie.title }}
<slot :id="movie.id"></slot>
</div>
</template>
<script>
export default {
props: ["movie"],
};
</script>
<style>
.movie {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding: 10px;
border: solid 1px #ddd;
}
</style>
运行结果:
总结:
- 子组件使用
props
接收父组件传递来的数据。 - 子组件使用
<slot :id="属性名"></slot>
的方式向父组件传递数据,父组件使用<template v-slot:插槽名="任意字符"></template>
的方式接收子组件传递的数据,任意字符
代表返回的数据,可以直接使用插值表达式调用。