1、什么是插槽
VUE官方文档的解释:
Vue 实现了一套内容分发的 API,将
元素作为承载分发内容的出口。
个人理解:
插槽实质是对子组件的扩展,通过
插槽向子组件内部指定位置传递内容。
或者这么说:
的出现是为了父组件可以堂而皇之地在子组件中加入内容。
打个比方:
有句话叫一个萝卜一个坑。父组件想要在子组件中种萝卜,需要在子组件中挖个坑,
就是一个【萝卜坑】。父组件想要给子组件添加的内容就是【萝卜】。
由此可见,萝卜种不种,种什么萝卜由父组件控制;萝卜坑在哪,由子组件控制。 换言之 ——
插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。
举个栗子,在 Father 父组件内部使用名为 Child 的子组件,向子组件的内部的“指定位置”传递一些内容,你可以这么写:
<!-- 这是子组件--> <div class="child"> <h2>Child的标题</h2> <slot>我是一个萝卜坑</slot> </div>
<!-- 这是父组件--> <div class="father"> <h1>Father的标题</h1> <Child> 我是个萝卜~ </Child> </div>
看到没,是不是就像父组件的萝卜种到了子组件的坑里?
看到这里不禁发出灵魂一问:这不就是父组件给子组件传了个值吗?那和props有啥区别?
2、与 props 的区别
通过props属性,父组件只能向子组件传递属性、方法
<template> <div> <h1>我是A</h1> <child :to-child-data="parentData" :to-child-fun="parentFun"></child> </div> </template> <script> import child from './B.vue' export default {
name:'A', components: {
child }, data () {
return {
say: '', parentData: "hello" }; }, methods: {
parentFun(val) {
//自定义的函数 val是子组件给的参数 this.say = val; //献给data一份问候~ console.log(val," from child"); //看看参数有没有值 console.log(this.say," from parent"); //看看能不能给父组件的data传进去 }, } }; </script>
<template> <div> <h2>我是B</h2> <el-button @click="childClick">子组件调用父组件的props方法</el-button> </div> </template> <script> export default {
name:'B', data () {
return {
}; }, props: {
toChildFun: {
type: Function, //参数类型:函数 required: true //是否必填:是 }, toChildData: {
type: String, //参数类型:String default: '' //默认值 } }, methods: {
childClick(event) {
this.$props.toChildFun('这是来自子组件的问候~~'); //调用Props传来的方法,并送他一个参数~~ // this.toChildFun('这是来自子组件的问候~~'); //跟上面一个效果 } } }; </script>


而插槽还可以传递带标签的内容、甚至是组件:
<!-- 这是父组件哦--> <div class="father"> <h1>Father的标题</h1> <Child> {
{
username}} <!-- 参数--> <button>我是一个按钮</button> <!-- 带标签的内容--> <Child2></Child2> <!-- 组件--> </Child> </div>
3、插槽的使用
(1)匿名插槽(又叫单个插槽、默认插槽)
就是没有设置name属性的插槽。
<slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot>
<div class="child"> <h1>子组件</h1> <slot name="head">头部默认值</slot> <slot name="body">主体默认值</slot> <slot>这是个匿名插槽(没有name属性),这串字符是匿名插槽的默认值。</slot> </div>
<div class="parent"> <h1>父组件</h1> <child> <p slot="body">我是主体</p> <p>我是其他内容</p> <p slot="footer">我是尾巴</p> </child> </div>
注意:
1、
我是尾巴
插槽被丢弃了,因为子组件中没有的插槽与之匹配。
2、 如果子组件中的匿名插槽不存在,则
我是其他内容
也会被丢弃。
这个例子就是想说明两点:
1、坑会一直在,但是没有找到坑的萝卜就会被丢弃!
2、后来者居上,后面的萝卜会覆盖原来坑里的萝卜
(2)具名插槽
意思就是具有名字的插槽,名字通过属性name来定义。
<slot name="body">这是个具名插槽(有name属性),这串字符是具名插槽的默认值。</slot>
一个组件中可以有很多具名插槽,出现在不同的位置。
<!-- <base-layout>组件--> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> <!-- 一个不带 name 的 <slot> 出口会带有隐含的名字“default”。--> </main> <footer> <slot name="footer"></slot> </footer> </div>
上面的那个例子,是直接把slot直接用在普通标签或者上:
我是主体
出资之外, 更推荐在 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供插槽名称,这样就可以定义插槽的内容了:
等于
插槽内容
插槽内容
<base-layout> <template v-slot:header> <h1>我是头header</h1> <!-- <h1 slot="header">我是头header</h1>--> </template> <p>我是main的内容111</p> <p>我也是main的内容222</p> <template v-slot:footer> <p>我是footer</p> <!-- <p slot="footer">我是footer</p>--> </template> </base-layout>
1、带有 v-slot 的 元素中的所有内容都将会被传入相应的插槽。
2、任何没有被包裹在带有 v-slot 的 中的内容都会被视为默认插槽的内容。
如果你希望更明确一些,可以在一个 中包裹默认插槽的内容:
<base-layout> <template v-slot:header> <h1>我是头header</h1> </template> <template v-slot:default> <p>我是main的内容111</p> <p>我也是main的内容222</p> </template> <template v-slot:footer> <p>我是footer</p> </template> </base-layout>
以上两种写法的渲染效果是一样的:
注意:slot 可以用在任何元素上,v-slot 只能添加在
上。 只有一种例外情况,请继续往下看。
(3)作用域插槽
上面props的例子,可以看到 父组件传给子组件了一个属性和一个方法,子组件可以使用 props 中的属性和方法。那对于插槽来说,父组件想访问子组件的数据,又该怎么做呢?
<!-- 这是子组件<Child> --> <template> <div> <h1>hey,我是组件Child的标题</h1> <slot></slot> </div> </template> <script> export default {
data() {
return {
childUser: {
Name:"Tom", Age: 23 } } } </script>
当Father使用Child组件时,想访问Child中的数据 childUser 并且将其展示在插槽的位置:
<!-- 这是父组件<Father>--> <div> <h1>hey,我是父组件Father的标题</h1> <Child> {
{
childUser.Name}} </Child> </div>
然而上述代码不会正常工作,因为
父级模板里的所有内容都是在父级作用域中编译的;子级模板里的所有内容都是在子作用域中编译的。
只有 组件可以访问到 childUser,而我们提供的内容【childUser.Name、childUser.Age】是在父级
中渲染的。
为了让 childUser 在父级的插槽内容中可用,需要把 childUser 从 子级作用域传递到
父级作用域。
做法就是将 childUser 作为 元素的一个属性绑定上去:
<template> <div> <h1>hey,我是组件Child的标题
h1> <slot v-bind:childData="childUser">
slot>
div>
template> <script> export default {
data() {
return {
childUser: {
Name:"Tom", Age:23 } } }
script>
绑定在
元素上的属性childData 被称为插槽 prop。
现在,在父级作用域中,我们可以使用带值的 v-slot 来定义 插槽 prop 的名字:
<div> <h1>hey,我是父组件Father的标题
h1> <Child> <template v-slot:default="slotProps"> {
{ slotProps.childData.Name}} {
{ slotProps.childData.Age}}
template>
Child>
div>
在这个例子中,我们将[ 包含所有插槽 prop 的对象 ] 命名为 slotProps(自定义)。
因为在上述情况下(这里就是上面说的那一种例外情况),被提供的内容只有默认插槽,组件的标签可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:
<div> <h1>hey,我是父组件Father的标题
h1> <Child v-slot:default="slotProps"> {
{ slotProps.childData.Name}} {
{ slotProps.childData.Age}}
Child>
div>
还可以省略default。就像未指明的内容对应默认插槽一样,不带参数的 v-slot 被假定对应默认插槽:
<div> <h1>hey,我是父组件Father的标题
h1> <Child v-slot="slotProps"> {
{ slotProps.childData.Name }} {
{ slotProps.childData.Age}}
Child>
div>
但是默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<Child v-slot="slotProps"> {
{ slotProps.childData.Name }} <template v-slot:other="otherSlotProps"> slotProps is NOT available here
template>
Child >
只要出现多个插槽,请始终为所有的插槽使用完整的基于 的语法:
也就是说,但只要出现多个插槽,所有的插槽名都得写全了,不能省略。
<template> <div> <h1>hey,我是组件Child的标题
h1> <slot v-bind:childData="childUser">
slot>
<slot name="other" v-bind:otherChildData="otherChildUser">
slot>
div>
template> <script> export default {
data() {
return{
childUser: {
Name:"Tom",Age:23}, otherChildUser:{
Name:"Mike",Age:25} } }
script>
<Child > <template v-slot:default="slotProps">
{
{ slotProps.childData.Name }}
template> <template v-slot:other="otherSlotProps"> {
{otherSlotProps.otherChildData.Name}}
template>
Child>
上面v-bind:childData="childUser",是将 childUser 作为
元素的一个属性绑定上去,感觉起的名太多了,有点眼花缭乱了,还能再简洁一点:
v-bind: 后面的参数是可以省略的:

<div> <h1>hey,我是组件Son的标题
h1> <slot v-bind="childUser">
slot>
div>
<div> <h1>hey,我是父组件Father的标题
h1> <Son> <template v-slot:default="user">
{
{ user.Name}}, {
{ user.Age}}
template>
Son>
div>
还可以省略的defalut:
<div> <h1>hey,我是父组件Father的标题
h1> <Son> <template v-slot="user">
{
{ user.Name}}, {
{ user.Age}}
template>
Son>
div>
(4)解构插槽 Prop
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里,
所以,这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。这样就很灵活。
<template> <div> <h1>hey,我是组件Child的标题
h1> <slot v-bind="childUser">
slot>
div>
template> <script> export default {
data() {
return {
childUser: {
Name:"Tom", Age:23 } } }
script>
<Child v-slot="{user}"> {
{ user.Name }}
Child>
还可以将 user重命名为 person:
<Child v-slot="{ user: person }"> {
{ person.Name }}
Child >
甚至可以定义默认内容,用于插槽 prop 是 undefined 的情形:
<Child v-slot="{ user= { Name: 'Guest' } }"> {
{ childData.Name }}
Child >
对象的解构赋值看不明呗的请移步 ====>> 变量的解构赋值—ES6入门,第六节。
4、v-slot 、 slot 、 slot-scope
(1) slot 和 slot-scope 已经被废弃
(2) 所有的 2.x 版本中, slot 和 slot-scope 属性仍会被支持
(3) Vue 3 不支持 slot 和 slot-scope,所以更推荐使用 v-slot
其实,v-slot就是把 slot 和 slot-scope 的功能整合了一下,把两个人的活都干了
插槽内容
v-slot 的使用
%ignore_pre_29%1、在一个
元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称。
%ignore_pre_30% %ignore_pre_31%2、只有 当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。 这样我们就可以把 v-slot 直接用在组件标签上。除此之外,
v-slot必须用在元素上。
也就是说,如果你希望使用缩写的话,#后面必须写插槽名,default不可以省略:
%ignore_pre_32%再来复习一下省略default:
%ignore_pre_33%slot 、 slot-scope 的使用
<template> <div> <h1>这里是子组件 h1> <slot name="mySlot" v-bind="childUser"> slot> div> template> <script> export default { name: 'Son', data() { return { childUser: { Name: "Tom", Age: 23 } } } } script><div> <h1>hey,我是父组件Father的标题 h1> <Son> <div slot="mySlot" slot-scope="data"> { { data.Name }} { { data.Age }} div> Son> div>1、 slot=“default” 可以省略不写,slot可以用在
元素,也可以用在任意的普通元素上。
2、这里的
slot-scope声明了被接收的prop对象会作为slotProps变量存在于作用域中。你可以像命名
JavaScript函数参数一样随意命名slotProps。同样的,slot-scope可以用在template元素,也可以用在任意的普通元素上。5、进阶
在循环中使用slot插槽时的传值方法、组件中遍历插槽
确定不点个赞再走?
欢迎交流讨论~
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201000.html原文链接:https://javaforall.net
