组件化是Vue中非常核心的概念,如果想要组件化,那必须要对组件进行封装。
而想要封装组件,那你一定要了解slot才能更好进行封装。
1.什么是插槽?
什么是插槽?请让我用一张图解释一下
概述一下,就是在对已经封装完的组件中插入自己想要定义的不同组件。
说白了,就是封装的组件中插入子组件,而子组件可以根据自己需求去定义。
而slot插槽有三种不同类型的插槽,分别为匿名插槽、具名插槽、作用域插槽。
2.匿名插槽
下面一段代码,教你快速使用匿名插槽
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template>
<p>插入匿名插槽</p>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
}
}
</script>
<!--封装的组件-->
<template>
<div class="child">
<h3>子组件:匿名插槽</h3>
<slot></slot>
</div>
</template>
<style scoped>
.child {
background: #fbd4fc;
}
</style>
从代码上看
其实就是在封装组件中,加 slot 标签
在要使用的标签上,使用 template 标签,再插入自己想要的组件。
当然,匿名插槽也可以叫默认插槽,这都是别名,不同的叫法。
3.具名插槽
先来看看具名插槽是怎么使用
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template v-slot:child>
<p>插入具名插槽</p>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
}
}
</script>
<!--封装的组件-->
<template>
<div class="child">
<h3>子组件:具名插槽</h3>
<slot name="child"></slot>
</div>
</template>
<style scoped>
.child {
background: #b2fffc;
}
</style>
通过代码可以很容看出
其实就是slot组件上多一个name属性
以及template模板上多了一个v-slot
这样就可以快速“对号入座”
⚠️不过要注意:本文代码基于 vue2.6.0+ 版本
看到这里,或许有小伙伴,有个困惑,具名插槽和默认插槽什么异同呢?
官方文档给出的解释:
下面我提供两个示例,大家一看就能懂
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template>
<p>如果不使用v-slot:就默认插入匿名插槽</p>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
}
}
</script>
<!--封装的组件-->
<template>
<div class="child">
<header>
<h3>头部div</h3>
<slot name="header"></slot>
</header>
<div class="content">
<h3>中间div</h3>
<!-- 等价于<slot></slot> -->
<slot name="default"></slot>
</div>
<footer>
<h3>尾部div</h3>
<slot name="footer"></slot>
</footer>
</div>
</template>
<style scoped>
header {
background: #a0c0ff;
}
.content {
background: #f8f59a;
}
footer {
background: #ffdfdf;
}
</style>
当然了,v-slot指令可以用#代替
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template #header>
<p>插入header</p>
</template>
<template>
<p>插入中间div</p>
</template>
<template #footer>
<p>插入footer</p>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
}
}
</script>
<!--封装的组件-->
<template>
<div class="child">
<header>
<h3>头部div</h3>
<slot name="header"></slot>
</header>
<div class="content">
<h3>中间div</h3>
<!-- 等价于<slot></slot> -->
<slot name="default"></slot>
</div>
<footer>
<h3>尾部div</h3>
<slot name="footer"></slot>
</footer>
</div>
</template>
<style scoped>
header {
background: #a0c0ff;
}
.content {
background: #f8f59a;
}
footer {
background: #ffdfdf;
}
</style>
4.作用域插槽
知道了匿名插槽和具名插槽是远远不够!
因为这两种插槽仅仅只能能把Dom插入到封装好的组件中,
而不能获取组件中的数据,这样有时候是满足不了我们的需求的。
如果想获取组件的数据,那么还是得靠作用域插槽!
先来一个例子说明下
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template slot-scope="childData">
<div v-for="(item, index) in childData.data" :key="index">
<input type="text" :value="item"/>
</div>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
}
}
</script>
<!--封装的组件-->
<template>
<div class="child">
<h3>child组件标题</h3>
<slot :data="list"></slot>
</div>
</template>
<script>
export default {
data () {
return {
list: ['1', '2', '3', '4', '5', '6', '7', '8', '9']
}
}
}
</script>
<style scoped>
</style>
看完这段代码,或许有些小伙伴就晕了。
别急,让我稍加说明!
其实简单的理解就是:封装的组件给要插入的组件传值
也可以说是变相的父给子组件传值
而template标签中自定义了 childData
childData 下的 data 也就是 组件内部的 list 数据
因为里面有段代码是 :data = "list"
这样一想简单易懂~
还有细心的小伙伴获取会注意到,我用了一个v-model!
试试例子,就会发现修改Input内容里面数据也会改变
那因为是input标签,vue中有封装自己的v-model
他可以双向绑定,那如果不是input标签,就可能需要自定义组件啦~
当然会有个办法提供给大家
<!--组件调用页面-->
<template>
<div class="parent">
<child>
<template slot-scope="childData">
<div v-for="(item, index) in childData.data.list" :key="index">
<input
type="text"
:value="item"
[@input](/user/input)="changeValue(childData.data.change, index, $event)"
/>
</div>
</template>
</child>
</div>
</template>
<script>
import Child from '@/components/Child'
export default {
components: {
Child
},
methods: {
/**
* 改变 input 的 value 方法
* @param change 子组件传入的函数方法(回调函数)
* @param index 索引值
* @param event 获取事件元素
*/
changeValue (change, index, event) {
console.log(111)
const value = event.currentTarget.value
change(index, value)
}
}
}
</script>
<template>
<div class="child">
<h3>child组件标题</h3>
<slot :data="{list, change: onChange}"></slot>
</div>
</template>
<script>
export default {
data () {
return {
list: ['1', '2', '3', '4', '5', '6', '7', '8', '9']
}
},
methods: {
/**
* 改变子组件 list 函数方法
* @param index 数组索引值
* @param value 新的 value 值
*/
onChange (index, value) {
this.list[index] = value
}
}
}
</script>
<style scoped>
</style>
简单的来说,就是在组件内部提供一个函数方法去让外部调用修改
如果你对这段话不太了解,那不妨看看另一篇简短而又干货满满的如何“修改”Vue中的prop~
感谢阅读
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 阿远Carry 原文链接:https://juejin.im/post/6858234998605938702